def rows_borders_added(self, links):
        """Add the links and the added row to the good rows

        :param links: Old links to reput
        """

        # Plus added to the last row
        sources_images_dir = Config().getSourceImageDir()
        add_search_bar_label = ClickableLabel()
        add_search_bar_label.setObjectName('plus')
        add_search_bar_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir,
                                         "green_plus.png")))
        add_search_bar_picture = add_search_bar_picture.scaledToHeight(20)
        add_search_bar_label.setPixmap(add_search_bar_picture)
        add_search_bar_label.clicked.connect(self.add_search_bar)
        self.rows[len(self.rows) - 1][6] = add_search_bar_label

        # Link added to every row, except the first one
        for i in range(1, len(self.rows)):
            row = self.rows[i]
            link_choice = QComboBox()
            link_choice.setObjectName('link')
            link_choice.addItem("AND")
            link_choice.addItem("OR")
            if len(links) >= i:
                link_choice.setCurrentText(links[i - 1])
            row[0] = link_choice
Beispiel #2
0
    def __init__(self, project, scan_list, main_window):
        """
        Initialization of the IterationTable widget.

        :param project: current project in the software
        :param scan_list: list of the selected database files
        :param main_window: software's main_window
        """

        QWidget.__init__(self)

        # Necessary for using MIA bricks
        ProcessMIA.project = project

        self.project = project

        if not scan_list:
            self.scan_list = self.project.session.get_documents_names(
                COLLECTION_CURRENT)
        else:
            self.scan_list = scan_list

        self.main_window = main_window
        self.iterated_tag = None

        # values_list will contain the different values of each selected tag
        self.values_list = [[], []]
        self.all_tag_values = []

        # Checkbox to choose to iterate the pipeline or not
        self.check_box_iterate = QCheckBox("Iterate pipeline")
        self.check_box_iterate.stateChanged.connect(
            self.emit_iteration_table_updated)

        # Label "Iterate over:"
        self.label_iterate = QLabel("Iterate over:")

        # Label that displays the name of the selected tag
        self.iterated_tag_label = QLabel("Select a tag")

        # Push button to select the tag to iterate
        self.iterated_tag_push_button = QPushButton("Select")
        self.iterated_tag_push_button.clicked.connect(
            self.select_iteration_tag)

        # QComboBox
        self.combo_box = QComboBox()
        self.combo_box.currentIndexChanged.connect(self.update_table)

        # filter
        self.filter_button = QPushButton('Filter')
        self.filter_button.clicked.connect(self.filter_values)

        # QTableWidget
        self.iteration_table = QTableWidget()

        # Label tag
        self.label_tags = QLabel("Tags to visualize:")

        # Each push button will allow the user to visualize a tag in
        # the iteration browser
        push_button_tag_1 = QPushButton()
        push_button_tag_1.setText("SequenceName")
        push_button_tag_1.clicked.connect(
            lambda: self.select_visualized_tag(0))

        push_button_tag_2 = QPushButton()
        push_button_tag_2.setText("AcquisitionDate")
        push_button_tag_2.clicked.connect(
            lambda: self.select_visualized_tag(1))

        # The list of all the push buttons
        # (the user can add as many as he or she wants)
        self.push_buttons = []
        self.push_buttons.insert(0, push_button_tag_1)
        self.push_buttons.insert(1, push_button_tag_2)

        # Labels to add/remove a tag (a push button)
        self.add_tag_label = ClickableLabel()
        self.add_tag_label.setObjectName('plus')
        sources_images_dir = Config().getSourceImageDir()
        add_tag_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir,
                                         "green_plus.png")))
        add_tag_picture = add_tag_picture.scaledToHeight(15)
        self.add_tag_label.setPixmap(add_tag_picture)
        self.add_tag_label.clicked.connect(self.add_tag)

        self.remove_tag_label = ClickableLabel()
        remove_tag_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir, "red_minus.png")))
        remove_tag_picture = remove_tag_picture.scaledToHeight(20)
        self.remove_tag_label.setPixmap(remove_tag_picture)
        self.remove_tag_label.clicked.connect(self.remove_tag)

        # Layout
        self.v_layout = QVBoxLayout()
        self.setLayout(self.v_layout)
        self.refresh_layout()
Beispiel #3
0
class IterationTable(QWidget):
    """Widget that handles pipeline iteration.

    .. Methods:
        - add_tag: adds a tag to visualize in the iteration table
        - emit_iteration_table_updated: emits a signal when the iteration
           scans have been updated
        - fill_values: fill values_list depending on the visualized tags
        - refresh_layout: updates the layout of the widget
        - remove_tag: removes a tag to visualize in the iteration table
        - select_iterated_tag: opens a pop-up to let the user select on which
           tag to iterate
        - select_visualized_tag: opens a pop-up to let the user select which
           tag to visualize in the iteration table
        - update_iterated_tag: updates the widget
        - update_table: updates the iteration table

    """

    iteration_table_updated = pyqtSignal(list, list)

    def __init__(self, project, scan_list, main_window):
        """
        Initialization of the IterationTable widget.

        :param project: current project in the software
        :param scan_list: list of the selected database files
        :param main_window: software's main_window
        """

        QWidget.__init__(self)

        # Necessary for using MIA bricks
        ProcessMIA.project = project

        self.project = project

        if not scan_list:
            self.scan_list = self.project.session.get_documents_names(
                COLLECTION_CURRENT)
        else:
            self.scan_list = scan_list

        self.main_window = main_window
        self.iterated_tag = None

        # values_list will contain the different values of each selected tag
        self.values_list = [[], []]
        self.all_tag_values = []

        # Checkbox to choose to iterate the pipeline or not
        self.check_box_iterate = QCheckBox("Iterate pipeline")
        self.check_box_iterate.stateChanged.connect(
            self.emit_iteration_table_updated)

        # Label "Iterate over:"
        self.label_iterate = QLabel("Iterate over:")

        # Label that displays the name of the selected tag
        self.iterated_tag_label = QLabel("Select a tag")

        # Push button to select the tag to iterate
        self.iterated_tag_push_button = QPushButton("Select")
        self.iterated_tag_push_button.clicked.connect(
            self.select_iteration_tag)

        # QComboBox
        self.combo_box = QComboBox()
        self.combo_box.currentIndexChanged.connect(self.update_table)

        # filter
        self.filter_button = QPushButton('Filter')
        self.filter_button.clicked.connect(self.filter_values)

        # QTableWidget
        self.iteration_table = QTableWidget()

        # Label tag
        self.label_tags = QLabel("Tags to visualize:")

        # Each push button will allow the user to visualize a tag in
        # the iteration browser
        push_button_tag_1 = QPushButton()
        push_button_tag_1.setText("SequenceName")
        push_button_tag_1.clicked.connect(
            lambda: self.select_visualized_tag(0))

        push_button_tag_2 = QPushButton()
        push_button_tag_2.setText("AcquisitionDate")
        push_button_tag_2.clicked.connect(
            lambda: self.select_visualized_tag(1))

        # The list of all the push buttons
        # (the user can add as many as he or she wants)
        self.push_buttons = []
        self.push_buttons.insert(0, push_button_tag_1)
        self.push_buttons.insert(1, push_button_tag_2)

        # Labels to add/remove a tag (a push button)
        self.add_tag_label = ClickableLabel()
        self.add_tag_label.setObjectName('plus')
        sources_images_dir = Config().getSourceImageDir()
        add_tag_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir,
                                         "green_plus.png")))
        add_tag_picture = add_tag_picture.scaledToHeight(15)
        self.add_tag_label.setPixmap(add_tag_picture)
        self.add_tag_label.clicked.connect(self.add_tag)

        self.remove_tag_label = ClickableLabel()
        remove_tag_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir, "red_minus.png")))
        remove_tag_picture = remove_tag_picture.scaledToHeight(20)
        self.remove_tag_label.setPixmap(remove_tag_picture)
        self.remove_tag_label.clicked.connect(self.remove_tag)

        # Layout
        self.v_layout = QVBoxLayout()
        self.setLayout(self.v_layout)
        self.refresh_layout()

    def add_tag(self):
        """Add a tag to visualize in the iteration table.

        Used only for tests
        """

        idx = len(self.push_buttons)
        push_button = QPushButton()
        push_button.setText('Tag n°' + str(len(self.push_buttons) + 1))
        push_button.clicked.connect(lambda: self.select_visualized_tag(idx))
        self.push_buttons.insert(len(self.push_buttons), push_button)
        self.refresh_layout()

    def emit_iteration_table_updated(self):
        """Emit a signal when the iteration scans have been updated."""
        if self.check_box_iterate.checkState():
            if hasattr(self, 'scans'):
                self.iteration_table_updated.emit(self.iteration_scans,
                                                  self.all_iterations_scans)
            else:
                self.iteration_table_updated.emit(self.scan_list,
                                                  [self.scan_list])
        else:
            self.iteration_table_updated.emit(self.scan_list, [self.scan_list])

    def fill_values(self, idx):
        """
        Fill values_list depending on the visualized tags

        :param idx: Index of the tag
        """
        tag_name = self.push_buttons[idx].text()
        values = []
        for scan in self.project.session.get_documents_names(
                COLLECTION_CURRENT):
            current_value = self.project.session.get_value(
                COLLECTION_CURRENT, scan, tag_name)
            if current_value is not None:
                values.append(current_value)

        idx_to_fill = len(self.values_list)
        while len(self.values_list) <= idx:
            self.values_list.insert(idx_to_fill, [])
            idx_to_fill += 1

        if self.values_list[idx] is not None:
            self.values_list[idx] = []

        for value in values:
            if value not in self.values_list[idx]:
                self.values_list[idx].append(value)

    def refresh_layout(self):
        """Update the layout of the widget.

        Called in widget's initialization and when a tag push button
        is added or removed.
        """

        first_v_layout = QVBoxLayout()
        first_v_layout.addWidget(self.check_box_iterate)

        second_v_layout = QVBoxLayout()
        second_v_layout.addWidget(self.label_iterate)
        second_v_layout.addWidget(self.iterated_tag_label)

        third_v_layout = QVBoxLayout()
        third_v_layout.addWidget(self.iterated_tag_push_button)
        hbox = QHBoxLayout()
        hbox.addWidget(self.combo_box)
        hbox.addWidget(self.filter_button)
        third_v_layout.addLayout(hbox)

        top_layout = QHBoxLayout()
        top_layout.addLayout(first_v_layout)
        top_layout.addLayout(second_v_layout)
        top_layout.addLayout(third_v_layout)

        self.v_layout.addLayout(top_layout)
        self.v_layout.addWidget(self.iteration_table)

        self.h_box = QHBoxLayout()
        self.h_box.setSpacing(10)
        self.h_box.addWidget(self.label_tags)

        for tag_label in self.push_buttons:
            self.h_box.addWidget(tag_label)

        self.h_box.addWidget(self.add_tag_label)
        self.h_box.addWidget(self.remove_tag_label)
        self.h_box.addStretch(1)

        self.v_layout.addLayout(self.h_box)

    def remove_tag(self):
        """Remove a tag to visualize in the iteration table.

        """
        if len(self.push_buttons) >= 1:
            push_button = self.push_buttons[-1]
            push_button.deleteLater()
            push_button = None
            del self.push_buttons[-1]
            del self.values_list[-1]
            self.refresh_layout()

    def select_iteration_tag(self):
        """Open a pop-up to let the user select on which tag to iterate."""

        ui_select = PopUpSelectTagCountTable(
            self.project,
            self.project.session.get_fields_names(COLLECTION_CURRENT),
            self.iterated_tag)

        if ui_select.exec_():

            if self.iterated_tag is None and ui_select.selected_tag is None:
                pass

            else:
                self.update_iterated_tag(ui_select.selected_tag)

    def filter_values(self):
        iterated_tag = self.iterated_tag
        tag_values = self.all_tag_values
        ui_iteration = PopUpSelectIteration(iterated_tag, tag_values)
        if ui_iteration.exec_():
            self.tag_values_list = [
                t.replace('&', '') for t in ui_iteration.final_values
            ]

            self.combo_box.clear()
            self.combo_box.addItems(self.tag_values_list)

            self.update_table()

    def select_visualized_tag(self, idx):
        """Open a pop-up to let the user select which tag to visualize in the
        iteration table.

        :param idx: index of the clicked push button
        """

        popUp = PopUpSelectTagCountTable(
            self.project,
            self.project.session.get_fields_names(COLLECTION_CURRENT),
            self.push_buttons[idx].text())
        if popUp.exec_() and popUp.selected_tag is not None:
            self.push_buttons[idx].setText(popUp.selected_tag)
            self.fill_values(idx)
            self.update_table()

    def update_iterated_tag(self, tag_name=None):
        """
        Update the widget when the iterated tag is modified.

        :param tag_name: name of the iterated tag
        """

        if len(self.main_window.pipeline_manager.scan_list) > 0:
            self.scan_list = self.main_window.pipeline_manager.scan_list
        else:
            self.scan_list = self.project.session.get_documents_names(
                COLLECTION_CURRENT)

        if tag_name is None:
            tag_name = self.iterated_tag
        self.iterated_tag_push_button.setText(tag_name)
        self.iterated_tag = tag_name
        self.iterated_tag_label.setText(tag_name + ":")

        # Update combo_box
        scans_names = self.project.session.get_documents_names(
            COLLECTION_CURRENT)
        scans_names = list(set(scans_names).intersection(self.scan_list))
        # tag_values_list contains all the values that can take iterated tag
        self.tag_values_list = []
        for scan_name in scans_names:
            tag_value = self.project.session.get_value(COLLECTION_CURRENT,
                                                       scan_name, tag_name)
            if str(tag_value) not in self.tag_values_list:
                self.tag_values_list.append(str(tag_value))
        # duplucate the values list to have the initial, unfiltered, one
        self.all_tag_values = list(self.tag_values_list)

        self.combo_box.clear()
        self.combo_box.addItems(self.tag_values_list)

        self.update_table()

    def update_table(self):
        """
        Update the iteration table.

        """

        # Updating the scan list
        if not self.scan_list:
            self.scan_list = self.project.session.get_documents_names(
                COLLECTION_CURRENT)

        # Clearing the table and preparing its columns
        self.iteration_table.clear()
        self.iteration_table.setColumnCount(len(self.push_buttons))

        # Headers
        for idx in range(len(self.push_buttons)):
            # FIXME should not use GUI text values !!
            header_name = self.push_buttons[idx].text().replace('&', '')
            if header_name not in self.project.session.get_fields_names(
                    COLLECTION_CURRENT):
                print("{0} not in the project's tags".format(header_name))
                return

            item = QTableWidgetItem()
            item.setText(header_name)
            self.iteration_table.setHorizontalHeaderItem(idx, item)

        # Searching the database scans that correspond to iterated tag value
        filter_query = "({" + str(self.iterated_tag) + "} " + "==" + " \"" + \
                       str(self.combo_box.currentText()).replace('&', '') + "\")"
        scans_list = self.project.session.filter_documents(
            COLLECTION_CURRENT, filter_query)
        scans_res = [
            getattr(document, TAG_FILENAME) for document in scans_list
        ]

        # Taking the intersection between the found database scans and the
        # user selection in the data_browser
        self.iteration_scans = list(
            set(scans_res).intersection(self.scan_list))
        self.iteration_table.setRowCount(len(self.iteration_scans))

        # Filling the table cells
        row = -1
        for scan_name in self.iteration_scans:
            row += 1
            for idx in range(len(self.push_buttons)):
                tag_name = self.push_buttons[idx].text().replace('&', '')

                item = QTableWidgetItem()
                item.setText(
                    str(
                        self.project.session.get_value(COLLECTION_CURRENT,
                                                       scan_name, tag_name)))
                self.iteration_table.setItem(row, idx, item)

        all_iterations_scans = []
        for tag_value in self.tag_values_list:
            # Searching the database scans that correspond to iterated tag value
            filter_query = "({" + str(self.iterated_tag) + "} " + "==" \
                  + " \"" + str(tag_value) + "\")"
            scans_list = self.project.session.filter_documents(
                COLLECTION_CURRENT, filter_query)
            scans_res = [
                getattr(document, TAG_FILENAME) for document in scans_list
            ]
            all_iterations_scans.append(
                list(set(scans_res).intersection(self.scan_list)))
        self.all_iterations_scans = all_iterations_scans
        #self.scans = True

        # This will change the scans list in the current Pipeline Manager tab
        self.iteration_table_updated.emit(self.iteration_scans,
                                          self.all_iterations_scans)
Beispiel #4
0
class CountTable(QDialog):
    """
    Tool to precisely verify the scans of the project.

    It is composed of push buttons on its top, each one corresponding to a tag
    selected by the user.
    When, the "Count scans" button is clicked, a table is created with all the
    combinations possible for the values of the first n-1 tags. Then,
    the m values that can take the  last tag are displayed in the header of
    the m last columns of the table. The cells are then filled with a green
    plus or a red cross depending on if there is at least a scan that has
    all the tags values or not.

    .. Methods:
        - add_tag: adds a tag to visualize in the count table
        - count_scans: counts the number of scans depending on the selected
          tags and displays the result in the table
        - fill_first_tags: fills the cells of the table corresponding to the
          (n-1) first selected tags
        - fill_headers: fills the headers of the table depending on the
          selected tags
        - fill_last_tag: fills the cells corresponding to the last selected tag
        - fill_values: fill values_list depending on the visualized tags
        - prepare_filter: prepares the filter in order to fill the count table
        - refresh_layout: updates the layout of the widget
        - remove_tag: removes a tag to visualize in the count table
        - select_tag: opens a pop-up to select which tag to visualize in
          the count table

    :Example:

    Assume that the current project has scans for two patients (P1 and P2)
    and three time points (T1, T2 and T3). For each (patient, time point),
    several sequences have been made (two RARE, one MDEFT and one FLASH).
    Selecting [PatientName, TimePoint, SequenceName] as tags, the table will
    be:

    +-------------+-----------+------+-------+-------+
    | PatientName | TimePoint | RARE | MDEFT | FLASH |
    +=============+===========+======+=======+=======+
    | P1          | T1        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P1          | T2        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P1          | T3        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P2          | T1        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P2          | T2        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P2          | T3        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+

    with v(n) meaning that n scans corresponds of the selected values for
    (PatientName, TimePoint,SequenceName).

    If no scans corresponds for a triplet value, a red cross will be
    displayed. For example, if the user forgets to import one RARE for P1 at
    T2 and one FLASH for P2 at T3. The table will be:

    +-------------+-----------+------+-------+-------+
    | PatientName | TimePoint | RARE | MDEFT | FLASH |
    +=============+===========+======+=======+=======+
    | P1          | T1        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P1          | T2        | v(1) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P1          | T3        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P2          | T1        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P2          | T2        | v(2) | v(1)  | v(1)  |
    +-------------+-----------+------+-------+-------+
    | P2          | T3        | v(2) | v(1)  | x     |
    +-------------+-----------+------+-------+-------+

    Thus, thanks to the CountTable tool, he or she directly knows if some
    scans are missing.

    """
    def __init__(self, project):
        """
        Initialization of the Count Table

        :param project: current project in the software
        """

        super().__init__()

        self.project = project

        # Issue #147: Allow to interact with database while the popup is open
        # self.setModal(True)
        self.setWindowTitle("Count table")

        # Font
        self.font = QFont()
        self.font.setBold(True)

        # values_list will contain the different values of each selected tag
        self.values_list = [[], []]

        self.label_tags = QLabel('Tags: ')

        # Each push button will allow the user to add a tag to the count table
        push_button_tag_1 = QPushButton()
        push_button_tag_1.setText("Tag n°1")
        push_button_tag_1.clicked.connect(lambda: self.select_tag(0))

        push_button_tag_2 = QPushButton()
        push_button_tag_2.setText("Tag n°2")
        push_button_tag_2.clicked.connect(lambda: self.select_tag(1))

        # The list of all the push buttons
        # (the user can add as many as he or she wants)
        self.push_buttons = []
        self.push_buttons.insert(0, push_button_tag_1)
        self.push_buttons.insert(1, push_button_tag_2)

        # Labels to add/remove a tag (a push button)
        sources_images_dir = Config().getSourceImageDir()
        self.remove_tag_label = ClickableLabel()
        remove_tag_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir, "red_minus.png")))
        remove_tag_picture = remove_tag_picture.scaledToHeight(20)
        self.remove_tag_label.setPixmap(remove_tag_picture)
        self.remove_tag_label.clicked.connect(self.remove_tag)

        self.add_tag_label = ClickableLabel()
        self.add_tag_label.setObjectName('plus')
        add_tag_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir,
                                         "green_plus.png")))
        add_tag_picture = add_tag_picture.scaledToHeight(15)
        self.add_tag_label.setPixmap(add_tag_picture)
        self.add_tag_label.clicked.connect(self.add_tag)

        # Push button that is pressed to launch the computations
        self.push_button_count = QPushButton()
        self.push_button_count.setText('Count scans')
        self.push_button_count.clicked.connect(self.count_scans)

        # The table that will be filled
        self.table = QTableWidget()

        # Layouts
        self.v_box_final = QVBoxLayout()
        self.setLayout(self.v_box_final)
        self.refresh_layout()

    def add_tag(self):
        """
        Adds a tag to visualize in the count table
        """

        idx = len(self.push_buttons)
        push_button = QPushButton()
        push_button.setText('Tag n°' + str(len(self.push_buttons) + 1))
        push_button.clicked.connect(lambda: self.select_tag(idx))
        self.push_buttons.insert(len(self.push_buttons), push_button)
        self.refresh_layout()

    def count_scans(self):
        """Counts the number of scans depending on the selected tags and
        displays the result in the table
        """

        for tag_values in self.values_list:
            if len(tag_values) == 0:
                return
        if self.project.session.get_field(
                COLLECTION_CURRENT, self.push_buttons[-1].text()) is None:
            return

        # Clearing the table
        self.table.clear()

        # nb_values will contain, for each index, the number of
        # different values that a tag can take
        self.nb_values = []
        for values in self.values_list:
            self.nb_values.append(len(values))

        # The number of rows will be the multiplication of all these
        # values
        self.nb_row = reduce(operator.mul, self.nb_values[:-1], 1)

        # The number of columns will be the addition of the number of
        # selected tags (minus 1) and the number of different values
        # that can take the last selected tag
        self.nb_col = len(self.values_list) - 1 + self.nb_values[-1]

        self.table.setRowCount(self.nb_row)
        self.table.setColumnCount(self.nb_col)

        self.fill_headers()
        self.fill_first_tags()
        self.fill_last_tag()

        self.table.resizeColumnsToContents()

    def fill_first_tags(self):
        """Fills the cells of the table corresponding to the (n-1)
           first selected tags
        """

        cell_text = []
        for col in range(len(self.values_list) - 1):
            # cell_text will contain the n-1 element to display
            cell_text.append(self.values_list[col][0])

            # Filling the last "Total" column
            item = QTableWidgetItem()
            item.setText(str(self.nb_values[col]))
            item.setFont(self.font)
            self.table.setItem(self.nb_row, col, item)

        # Filling the cells of the n-1 first tags
        for row in range(self.nb_row):

            for col in range(len(self.values_list) - 1):
                item = QTableWidgetItem()
                tag_name = self.push_buttons[col].text()
                tag_type = self.project.session.get_field(
                    COLLECTION_CURRENT, tag_name).field_type
                set_item_data(item, cell_text[col], tag_type)
                self.table.setItem(row, col, item)

            # Looping from the (n-1)th tag
            col_checked = len(self.values_list) - 2
            # Flag up will be True when all values of the tag
            # have been iterated
            flag_up = False
            while col_checked >= 0:
                if flag_up:
                    # In this case, the value of the right column has
                    # reach its last value
                    # This value has been reset to the first value
                    if (cell_text[col_checked] == self.values_list[col_checked]
                        [-1]):
                        # If the value that has been displayed is the
                        # last one, the flag stays the same, the value of
                        # the column on the left has to be changed
                        cell_text[col_checked] = self.values_list[col_checked][
                            0]
                    else:
                        # Else we iterate on the next value
                        idx = self.values_list[col_checked].index(
                            cell_text[col_checked])
                        cell_text[col_checked] = self.values_list[col_checked][
                            idx + 1]
                        flag_up = False

                if (col_checked > 0 and len(self.values_list) - 1 > 1) or (
                        len(self.values_list) - 1 == 1):
                    if cell_text[col_checked] == self.values_list[col_checked][
                            -1]:
                        # If the value that has been displayed is the last one,
                        # the flag is set to True, the value of the column
                        # on the left has to be changed
                        cell_text[col_checked] = self.values_list[col_checked][
                            0]
                        flag_up = True
                    else:
                        # Else we iterate on the next value and reset the flag
                        idx = self.values_list[col_checked].index(
                            cell_text[col_checked])
                        cell_text[col_checked] = self.values_list[col_checked][
                            idx + 1]
                        flag_up = False

                    if not flag_up:
                        # If there is nothing to do, we quit the loop
                        break

                col_checked -= 1

    def fill_headers(self):
        """
        Fills the headers of the table depending on the selected tags
        """

        idx_end = 0
        # Headers
        for idx in range(len(self.values_list) - 1):
            header_name = self.push_buttons[idx].text()
            item = QTableWidgetItem()
            item.setText(header_name)
            self.table.setHorizontalHeaderItem(idx, item)
            idx_end = idx

        # idx_last_tag corresponds to the index of the (n-1)th tag
        self.idx_last_tag = idx_end
        last_tag = self.push_buttons[len(self.values_list) - 1].text()
        last_tag_type = self.project.session.get_field(COLLECTION_CURRENT,
                                                       last_tag).field_type
        for header_name in self.values_list[-1]:
            idx_end += 1
            item = QTableWidgetItem()
            set_item_data(item, header_name, last_tag_type)
            self.table.setHorizontalHeaderItem(idx_end, item)

        # Adding a "Total" row and to count the scans
        self.table.insertRow(self.nb_row)
        item = QTableWidgetItem()
        item.setText('Total')

        item.setFont(self.font)

        self.table.setVerticalHeaderItem(self.nb_row, item)

    def fill_last_tag(self):
        """
        Fills the cells corresponding to the last selected tag
        """

        # Cells of the last tag
        for col in range(self.idx_last_tag + 1, self.nb_col):
            nb_scans_ok = 0
            # Creating a tag_list that will contain
            # couples tag_name/tag_value that
            # will then querying the Database
            for row in range(self.nb_row):
                tag_list = []
                for idx_first_columns in range(self.idx_last_tag + 1):
                    tag_name = self.table.horizontalHeaderItem(
                        idx_first_columns).text()
                    tag_type = self.project.session.get_field(
                        COLLECTION_CURRENT, tag_name).field_type
                    value_str = self.table.item(row, idx_first_columns).data(
                        Qt.EditRole)
                    value_database = table_to_database(value_str, tag_type)
                    tag_list.append([tag_name, value_database])
                tag_last_columns = self.push_buttons[-1].text()
                tag_last_columns_type = self.project.session.get_field(
                    COLLECTION_CURRENT, tag_last_columns).field_type
                value_last_columns_str = self.table.horizontalHeaderItem(
                    col).data(Qt.EditRole)
                value_last_columns_database = table_to_database(
                    value_last_columns_str, tag_last_columns_type)
                tag_list.append(
                    [tag_last_columns, value_last_columns_database])

                item = QTableWidgetItem()
                item.setFlags(QtCore.Qt.ItemIsEnabled)
                # Getting the list of the scans that corresponds to the couples
                # tag_name/tag_values
                generator_scans = self.project.session.filter_documents(
                    COLLECTION_CURRENT, self.prepare_filter(tag_list))

                # List of scans created, given the generator
                list_scans = [
                    getattr(scan, TAG_FILENAME) for scan in generator_scans
                ]

                sources_images_dir = Config().getSourceImageDir()
                if list_scans:
                    icon = QIcon(
                        os.path.join(sources_images_dir, 'green_v.png'))
                    length = len(list_scans)
                    nb_scans_ok += length
                    text = str(length)
                    item.setText(text)
                    tool_tip = ''
                    # Setting as tooltip all the corresponding scans
                    for tpl in list_scans:
                        tool_tip += (tpl + '\n')
                    tool_tip = tool_tip[:-1]
                    item.setToolTip(tool_tip)
                else:
                    icon = QIcon(
                        os.path.join(sources_images_dir, 'red_cross.png'))
                item.setIcon(icon)
                self.table.setItem(row, col, item)

            item = QTableWidgetItem()
            item.setText(str(nb_scans_ok))
            item.setFont(self.font)
            self.table.setItem(self.nb_row, col, item)

    def fill_values(self, idx):
        """
        Fill values_list depending on the visualized tags

        :param idx: index of the select tag
        """

        tag_name = self.push_buttons[idx].text()
        values = []
        for scan in self.project.session.get_documents_names(
                COLLECTION_CURRENT):
            current_value = self.project.session.get_value(
                COLLECTION_CURRENT, scan, tag_name)
            if current_value is not None:
                values.append(current_value)

        idx_to_fill = len(self.values_list)
        while len(self.values_list) <= idx:
            self.values_list.insert(idx_to_fill, [])
            idx_to_fill += 1

        if self.values_list[idx] is not None:
            self.values_list[idx] = []

        for value in values:
            if value not in self.values_list[idx]:
                self.values_list[idx].append(value)

    @staticmethod
    def prepare_filter(couples):
        """
        Prepares the filter in order to fill the count table

        :param couples: (tag, value) couples
        :return: Str query of the corresponding filter
        """

        query = ""

        and_to_write = False

        for couple in couples:
            tag = couple[0]
            value = couple[1]

            # No AND for the first condition
            if and_to_write:
                query += " AND "

            and_to_write = True

            if isinstance(value, list):
                query += "({" + tag + "} == " + str(value) + ")"

            else:
                query += "({" + tag + "} == \"" + str(value) + "\")"

        query = "(" + query + ")"

        return query

    def refresh_layout(self):
        """
        Updates the layout of the widget
        """

        self.h_box_top = QHBoxLayout()
        self.h_box_top.setSpacing(10)
        self.h_box_top.addWidget(self.label_tags)

        for tag_label in self.push_buttons:
            self.h_box_top.addWidget(tag_label)

        self.h_box_top.addWidget(self.add_tag_label)
        self.h_box_top.addWidget(self.remove_tag_label)
        self.h_box_top.addWidget(self.push_button_count)
        self.h_box_top.addStretch(1)

        self.v_box_final.addLayout(self.h_box_top)
        self.v_box_final.addWidget(self.table)

    def remove_tag(self):
        """
        Removes a tag to visualize in the count table
        """

        push_button = self.push_buttons[-1]
        push_button.deleteLater()
        push_button = None
        del self.push_buttons[-1]
        del self.values_list[-1]
        self.refresh_layout()

    def select_tag(self, idx):
        """
        Opens a pop-up to select which tag to visualize in the count table

        :param idx: the index of the selected push button
        """

        pop_up = PopUpSelectTagCountTable(
            self.project,
            self.project.session.get_fields_names(COLLECTION_CURRENT),
            self.push_buttons[idx].text())
        if pop_up.exec_():
            if pop_up.selected_tag is not None:
                self.push_buttons[idx].setText(pop_up.selected_tag)
                self.fill_values(idx)
Beispiel #5
0
    def __init__(self, project):
        """
        Initialization of the Count Table

        :param project: current project in the software
        """

        super().__init__()

        self.project = project

        # Issue #147: Allow to interact with database while the popup is open
        # self.setModal(True)
        self.setWindowTitle("Count table")

        # Font
        self.font = QFont()
        self.font.setBold(True)

        # values_list will contain the different values of each selected tag
        self.values_list = [[], []]

        self.label_tags = QLabel('Tags: ')

        # Each push button will allow the user to add a tag to the count table
        push_button_tag_1 = QPushButton()
        push_button_tag_1.setText("Tag n°1")
        push_button_tag_1.clicked.connect(lambda: self.select_tag(0))

        push_button_tag_2 = QPushButton()
        push_button_tag_2.setText("Tag n°2")
        push_button_tag_2.clicked.connect(lambda: self.select_tag(1))

        # The list of all the push buttons
        # (the user can add as many as he or she wants)
        self.push_buttons = []
        self.push_buttons.insert(0, push_button_tag_1)
        self.push_buttons.insert(1, push_button_tag_2)

        # Labels to add/remove a tag (a push button)
        sources_images_dir = Config().getSourceImageDir()
        self.remove_tag_label = ClickableLabel()
        remove_tag_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir, "red_minus.png")))
        remove_tag_picture = remove_tag_picture.scaledToHeight(20)
        self.remove_tag_label.setPixmap(remove_tag_picture)
        self.remove_tag_label.clicked.connect(self.remove_tag)

        self.add_tag_label = ClickableLabel()
        self.add_tag_label.setObjectName('plus')
        add_tag_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir,
                                         "green_plus.png")))
        add_tag_picture = add_tag_picture.scaledToHeight(15)
        self.add_tag_label.setPixmap(add_tag_picture)
        self.add_tag_label.clicked.connect(self.add_tag)

        # Push button that is pressed to launch the computations
        self.push_button_count = QPushButton()
        self.push_button_count.setText('Count scans')
        self.push_button_count.clicked.connect(self.count_scans)

        # The table that will be filled
        self.table = QTableWidget()

        # Layouts
        self.v_box_final = QVBoxLayout()
        self.setLayout(self.v_box_final)
        self.refresh_layout()
Beispiel #6
0
    def show_slices(self, file_paths):
        """Creates the thumbnails from the selected file paths.

        :param file_paths: the selected file paths
        """

        # If it's the first time that this function is called, the MiniViewer
        # has to be shown
        if self.first_time:
            self.setHidden(False)
            self.first_time = False

        # If the user has willingly hidden the MiniViewer, the Processes are
        # not made
        if self.isHidden():
            pass
        else:
            self.do_nothing = [False] * len(file_paths)

            self.file_paths = file_paths
            self.max_scans = len(file_paths)

            self.setMinimumHeight(220)

            self.clearLayouts()

            self.frame = QFrame(self)
            self.frame_final = QFrame(self)

            # Limiting the legend of the thumbnails
            self.nb_char_max = 60

            font = QFont()
            font.setPointSize(9)

            # Reading the images from the file paths
            for idx, file_path in enumerate(self.file_paths.copy()):
                try:
                    self.img.insert(idx, nib.load(file_path))
                except nib.filebasedimages.ImageFileError:
                    print("Error while trying to display the image " +
                          file_path)
                    self.file_paths.remove(file_path)
                except FileNotFoundError:
                    print("File " + file_path + " not existing")
                    self.file_paths.remove(file_path)

            # If we are in the "cursors" display mode
            if self.check_box_slices.checkState() == Qt.Unchecked:

                # Layout to aligne each thumbnail (image + cursors)
                self.h_box_thumb = QHBoxLayout()

                # idx represents the index of the selected image
                for idx in range(min(self.max_scans, len(self.file_paths))):
                    if not self.do_nothing[idx]:

                        # Creating sliders and labels
                        self.boxSlider(idx)
                        self.enableSliders(idx)
                        self.createDimensionLabels(idx)

                        # Getting the sliders value and reading the image data
                        self.indexImage(idx)

                        # Making some pixel modification to display the image
                        # correctly
                        self.image2DModifications(idx)

                    self.displayPosValue(idx)

                    w, h = self.im_2D[idx].shape

                    im_Qt = QImage(self.im_2D[idx].data, w, h,
                                   QImage.Format_Indexed8)
                    pixm = QPixmap.fromImage(im_Qt)

                    file_path_base_name = os.path.basename(
                        self.file_paths[idx])

                    # imageLabel is the label where the image is displayed
                    # (as a pixmap)
                    self.imageLabel.insert(idx, QLabel(self))
                    self.imageLabel[idx].setPixmap(pixm)
                    self.imageLabel[idx].setToolTip(file_path_base_name)

                    self.label_description.insert(idx, ClickableLabel())
                    self.label_description[idx].setFont(font)
                    self.label_description[idx].clicked.connect(
                        self.openTagsPopUp)

                    # Looking for the tag value to display as a
                    # legend of the thumbnail
                    file_path_base_name = os.path.relpath(
                        self.file_paths[idx], self.project.folder)
                    self.setThumbnail(file_path_base_name, idx)

                    # Layout that corresponds to the 3D dimension
                    self.h_box_slider_3D = QHBoxLayout()
                    self.h_box_slider_3D.addWidget(self.label3D[idx])
                    self.h_box_slider_3D.addWidget(self.txt_slider_3D[idx])
                    self.h_box_slider_3D.addWidget(self.slider_3D[idx])

                    # Layout that corresponds to the 4D dimension
                    self.h_box_slider_4D = QHBoxLayout()
                    self.h_box_slider_4D.addWidget(self.label4D[idx])
                    self.h_box_slider_4D.addWidget(self.txt_slider_4D[idx])
                    self.h_box_slider_4D.addWidget(self.slider_4D[idx])

                    # Layout that corresponds to the 5D dimension
                    self.h_box_slider_5D = QHBoxLayout()
                    self.h_box_slider_5D.addWidget(self.label5D[idx])
                    self.h_box_slider_5D.addWidget(self.txt_slider_5D[idx])
                    self.h_box_slider_5D.addWidget(self.slider_5D[idx])

                    # Layout for the three sliders
                    self.v_box_sliders = QVBoxLayout()
                    self.v_box_sliders.addLayout(self.h_box_slider_3D)
                    self.v_box_sliders.addLayout(self.h_box_slider_4D)
                    self.v_box_sliders.addLayout(self.h_box_slider_5D)

                    # Layout that corresponds to the image + the sliders
                    self.h_box = QHBoxLayout()
                    self.h_box.addWidget(self.imageLabel[idx])
                    self.h_box.addLayout(self.v_box_sliders)

                    # Layout that corresponds to the image and sliders +
                    # the description
                    self.v_box_thumb = QVBoxLayout()
                    self.v_box_thumb.addLayout(self.h_box)
                    self.v_box_thumb.addWidget(self.label_description[idx])

                    # Layout that will contain all the thumbnails
                    self.h_box_thumb.addLayout(self.v_box_thumb)

                self.frame.setLayout(self.h_box_thumb)

            # If we are in the "all slices" display mode
            else:

                self.h_box_images = QHBoxLayout()
                self.h_box_images.setSpacing(10)
                self.v_box_scans = QVBoxLayout()

                # idx represents the index of the selected image
                for idx in range(len(self.file_paths)):
                    file_path_base_name = os.path.relpath(
                        self.file_paths[idx], self.project.folder)

                    self.label_description.insert(idx, ClickableLabel())
                    self.label_description[idx].setFont(font)
                    self.label_description[idx].clicked.connect(
                        self.openTagsPopUp)

                    # Looking for the tag value to display as a legend
                    # of the thumbnail
                    self.setThumbnail(file_path_base_name, idx)

                    # Depending of the dimension of the image,
                    # the legend of each image and the number of images to
                    # display will change
                    if not self.do_nothing[idx]:
                        if len(self.img[idx].shape) == 3:
                            nb_slices = self.img[idx].shape[2]
                            txt = "Slice n°"
                        elif len(self.img[idx].shape) == 4:
                            nb_slices = self.img[idx].shape[3]
                            txt = "Time n°"
                        elif len(self.img[idx].shape) == 5:
                            nb_slices = self.img[idx].shape[4]
                            txt = "Study n°"
                        else:
                            nb_slices = 0

                        # Limiting the number of images to the number
                        # chosen by the user
                        for i in range(
                                min(nb_slices,
                                    int(self.line_edit_nb_slices.text()))):
                            pixm = self.image_to_pixmap(self.img[idx], i)

                            self.v_box = QVBoxLayout()

                            # label corresponds to the label where one image
                            # is displayed
                            label = QLabel(self)
                            label.setPixmap(pixm)
                            label.setToolTip(
                                os.path.basename(self.file_paths[idx]))

                            # Legend of the image (depends on the number
                            # of dimensions)
                            label_info = QLabel()
                            label_info.setFont(font)
                            label_info.setText(txt + str(i + 1))
                            label_info.setAlignment(QtCore.Qt.AlignCenter)

                            self.v_box.addWidget(label)
                            self.v_box.addWidget(label_info)

                            # This layout allows to chain each image
                            self.h_box_images.addLayout(self.v_box)
                        self.v_box_scans.addLayout(self.h_box_images)
                        self.v_box_scans.addWidget(self.label_description[idx])
                self.frame.setLayout(self.v_box_scans)

            # Adding a scroll area if the thumbnails are too large
            self.scroll_area = QScrollArea()
            self.scroll_area.setWidget(self.frame)

            self.h_box_check_box = QHBoxLayout()

            if self.check_box_slices.isChecked():
                self.h_box_check_box.addStretch(1)
                self.label_nb_slices.setHidden(False)
                self.line_edit_nb_slices.setHidden(False)
                self.h_box_check_box.addWidget(self.label_nb_slices)
                self.h_box_check_box.addWidget(self.line_edit_nb_slices)
                self.check_box_cursors.setHidden(True)
            else:
                self.check_box_cursors.setHidden(False)
                self.h_box_check_box.addWidget(self.check_box_cursors)
                self.h_box_check_box.addStretch(1)
                self.label_nb_slices.setHidden(True)
                self.line_edit_nb_slices.setHidden(True)

            self.h_box_check_box.addWidget(self.check_box_slices)

            self.v_box_final.addLayout(self.h_box_check_box)
            self.v_box_final.addWidget(self.scroll_area)
    def add_search_bar(self):
        """Create and define the advanced research bar."""
        row_layout = []

        # NOT choice
        not_choice = QComboBox()
        not_choice.setObjectName('not')
        not_choice.addItem("")
        not_choice.addItem("NOT")

        # Field choice
        field_choice = QComboBox()
        field_choice.setObjectName('field')
        if len(self.tags_list) > 0:
            for tag in self.tags_list:
                field_choice.addItem(tag)
        else:
            for tag in self.project.session.get_shown_tags():
                field_choice.addItem(tag)
        field_choice.model().sort(0)
        field_choice.addItem("All visualized tags")

        # Value choice
        condition_value = QLineEdit()
        condition_value.setObjectName('value')

        # Condition choice
        condition_choice = QComboBox()
        condition_choice.setObjectName('condition')
        condition_choice.addItem("==")
        condition_choice.addItem("!=")
        condition_choice.addItem(">=")
        condition_choice.addItem("<=")
        condition_choice.addItem(">")
        condition_choice.addItem("<")
        condition_choice.addItem("BETWEEN")
        condition_choice.addItem("IN")
        condition_choice.addItem("CONTAINS")
        condition_choice.addItem("HAS VALUE")
        condition_choice.addItem("HAS NO VALUE")
        condition_choice.model().sort(0)

        # Signal to update the placeholder text of the value
        condition_choice.currentTextChanged.connect(
            lambda: self.displayValueRules(condition_choice, condition_value))

        # Signal to update the list of conditions, depending on the tag type
        field_choice.currentTextChanged.connect(
            lambda: self.displayConditionRules(field_choice, condition_choice))

        # Minus to remove the row
        sources_images_dir = Config().getSourceImageDir()
        remove_row_label = ClickableLabel()
        remove_row_picture = QPixmap(
            os.path.relpath(os.path.join(sources_images_dir, "red_minus.png")))
        remove_row_picture = remove_row_picture.scaledToHeight(30)
        remove_row_label.setPixmap(remove_row_picture)

        # Everything appended to the row
        row_layout.append(None)  # Link room
        row_layout.append(not_choice)
        row_layout.append(field_choice)
        row_layout.append(condition_choice)
        row_layout.append(condition_value)
        row_layout.append(remove_row_label)
        row_layout.append(None)  # Add row room

        # Signal to remove the row
        remove_row_label.clicked.connect(lambda: self.remove_row(row_layout))

        self.rows.append(row_layout)

        self.refresh_search()

        self.displayConditionRules(field_choice, condition_choice)