Example #1
0
class ScriptsMenu(QWidget):
    def interface_check(function):
        def wrapper(self):
            function(self)
            try:
                self.on_block()
            except:
                return wrapper

        return wrapper

    def __init__(self):
        super().__init__()
        self.layout = QGridLayout()
        self.setLayout(self.layout)

        row_hight = 30
        margins = self.layout.contentsMargins()
        margins.setTop(row_hight)
        margins.setRight(20)
        self.layout.setContentsMargins(margins)

        # Initializing GUI elements
        self.settings_label = QLabel('Settings')
        self.platform_var_list = QComboBox()
        self.thread_var_list = QComboBox()

        self.intel64 = QCheckBox('Intel 64')
        self.IA32 = QCheckBox('IA32')
        self.TL = QCheckBox('Threading layer')
        self.omp = QRadioButton('OpenMP')
        self.tbb = QRadioButton('TBB')

        self.lib_name = QLineEdit()
        self.functions_list = QListWidget()
        self.save_build_script = QPushButton('Save build script')
        self.functions_names = []
        self.status_receiver = None

        self.lib_name.setPlaceholderText('Library name...')

        # Preparing elements by giving initial values and etc
        self.settings_label.setAlignment(Qt.AlignBottom)
        self.save_build_script.setDisabled(True)

        self.save_build_script.clicked.connect(self.on_save_build_script)
        self.IA32.clicked.connect(self.on_block)
        self.intel64.clicked.connect(self.on_block)
        self.TL.clicked.connect(self.set_tl)
        self.lib_name.textEdited.connect(self.on_block)
        self.platform_var_list.currentIndexChanged.connect(self.on_target_system_selection)

        # Setting all widgets in their places
        self.layout.addWidget(self.settings_label, 0, 0)

        self.layout.addWidget(self.TL, 1, 0)
        self.layout.addWidget(self.omp, 2, 0)
        self.layout.addWidget(self.tbb, 3, 0)

        self.layout.addWidget(self.intel64, 2, 1)
        self.layout.addWidget(self.IA32, 3, 1)
        self.layout.addWidget(self.platform_var_list, 2, 2, 1, 2)
        self.layout.addWidget(self.thread_var_list, 3, 2, 1, 2)

        self.layout.addWidget(self.lib_name, 4, 0, 1, 4)
        self.layout.addWidget(self.functions_list, 5, 0, 1, 4)
        self.layout.addWidget(self.save_build_script, 6, 0, 1, 4)

        self.settings_label.setFixedHeight(row_hight)
        self.TL.setFixedHeight(row_hight)
        self.platform_var_list.setFixedHeight(row_hight)
        self.thread_var_list.setFixedHeight(row_hight)

        self.__post_check()

    def set_configs(self, package):
        self.check_configs(package)

        self.TL.setChecked(settings.CONFIGS[package]['TL'])
        self.omp.setChecked(settings.CONFIGS[package]['OpenMP'])
        self.tbb.setChecked(settings.CONFIGS[package]['TBB'])
        self.set_tl()

        self.intel64.setChecked(settings.CONFIGS[package]['intel64'])
        self.IA32.setChecked(settings.CONFIGS[package]['IA32'])

        if self.intel64.isEnabled():
            if not self.intel64.isChecked() and not self.IA32.isChecked():
                self.intel64.setChecked(True)
        elif self.IA32.isEnabled():
            self.IA32.setChecked(True)

        self.thread_var_list.setCurrentText(utils.MULTI_THREADED) if settings.CONFIGS[package]['Multi-threaded'] \
            else self.thread_var_list.setCurrentText(utils.SINGLE_THREADED)

        self.functions_names = settings.CONFIGS[package]['functions_list']

    def check_configs(self, package):
        self.intel64_libs_path = self.parent.source.path_to_libs[package][utils.INTEL64]
        self.ia32_libs_path = self.parent.source.path_to_libs[package][utils.IA32]

        if self.intel64_libs_path:
            self.intel64.setEnabled(True)
        else:
            settings.CONFIGS[package]['intel64'] = False
            self.intel64.setDisabled(True)

        if self.ia32_libs_path:
            self.IA32.setEnabled(True)
        else:
            settings.CONFIGS[package]['IA32'] = False
            self.IA32.setDisabled(True)

        if self.check_dir('tl'):
            self.TL.setEnabled(True)
        else:
            settings.CONFIGS[package]['TL'] = False
            self.TL.setDisabled(True)

        self.thread_var_list.clear()
        if self.check_dir('threaded'):
            self.thread_var_list.addItems([utils.SINGLE_THREADED,
                                           utils.MULTI_THREADED])
        else:
            self.thread_var_list.addItem(utils.SINGLE_THREADED)
            settings.CONFIGS[package]['Multi-threaded'] = False

    def check_dir(self, dir):
        return os.path.exists(os.path.join(self.intel64_libs_path, dir)) or \
                os.path.exists(os.path.join(self.ia32_libs_path, dir))

    def get_configs(self, package):
        if self.TL.isEnabled():
            settings.CONFIGS[package]['TL'] = self.TL.isChecked()

        if settings.CONFIGS[package]['TL']:
            settings.CONFIGS[package]['OpenMP'] = self.omp.isChecked()
            settings.CONFIGS[package]['TBB'] = self.tbb.isChecked()

        settings.CONFIGS[package]['intel64'] = self.intel64.isChecked()
        settings.CONFIGS[package]['IA32'] = self.IA32.isChecked()

        settings.CONFIGS[package]['Multi-threaded'] = (self.thread_var_list.currentText() == utils.MULTI_THREADED)

        settings.CONFIGS[package]['functions_list'] = self.functions_names

    def set_status_output(self, status_receiver):
        self.status_receiver = status_receiver

    def __get_interface_state(self):
        return {settings.PACKAGE: self.parent.source.ipp.isEnabled() or self.parent.source.ippcp.isEnabled(),
                settings.PLATFORM: self.IA32.isChecked() or self.intel64.isChecked(),
                settings.LIB_NAME: bool(self.lib_name.text()),
                settings.FUNCTIONS: bool(self.functions_names),
                settings.ANDK: not ((not bool(utils.ANDROID_NDK_PATH))
                           and self.platform_var_list.currentText() == utils.ANDROID)
                }

    def on_item_selected(self, item):
        self.functions_list.setCurrentItem(item)

    def on_block(self):
        autobuild_requrements = settings.AUTOBUILD_BUTTON_RULES
        script_requrements = settings.SCRIPT_BUTTON_GENERATOR_RULES
        interface_state = self.__get_interface_state()
        if autobuild_requrements == interface_state:
            self.parent.set_auto_build_disabled(False)
            self.status_receiver.showMessage("Ready to build custom library")
        else:
            self.parent.set_auto_build_disabled(True)
            differences = dict(autobuild_requrements.items() - interface_state.items())
            self.status_receiver.showMessage("Set " + sorted(differences, key=len)[0])

        if script_requrements == {i: interface_state.get(i)
                                  for i in script_requrements.keys()}:
            self.save_build_script.setDisabled(False)
        else:
            self.save_build_script.setDisabled(True)

    def set_tl(self):
        if self.TL.isEnabled():
            self.set_threading_layer_enabled(self.TL.isChecked())
            self.parent.source.tl_selected = self.TL.isChecked()
        else:
            self.set_threading_layer_enabled(False)
            self.parent.source.tl_selected = False

        if self.parent.source.ipp.isChecked():
            self.parent.source.show_menu(utils.IPP)

        if not self.omp.isChecked() and not self.tbb.isChecked():
            if self.omp.isEnabled():
                self.omp.setChecked(True)
            elif self.tbb.isEnabled():
                self.tbb.setChecked(True)

    def set_threading_layer_enabled(self, bool):
        self.omp.setEnabled(bool) if self.check_dir(os.path.join('tl', 'openmp')) else self.omp.setDisabled(True)
        self.tbb.setEnabled(bool) if self.check_dir(os.path.join('tl', 'tbb')) else self.tbb.setDisabled(True)

    def on_save_build_script(self):
        library_path = QFileDialog.getExistingDirectory(self, 'Select a folder')
        if library_path == '':
            return
        host_system = utils.HOST_SYSTEM
        target_system = self.platform_var_list.currentText()
        functions = self.functions_names
        library_name = self.lib_name.text()
        threading = self.thread_var_list.currentText() == 'Multi-threaded'
        threading_layer_type = self.parent.get_treading_layer_type()

        if self.parent.source.ipp.isChecked():
            package = utils.IPP
            root = utils.IPPROOT
        else:
            package = utils.IPPCP
            root = utils.IPPCRYPTOROOT

        os.environ[root] = settings.CONFIGS[package]['Path']

        if self.IA32.isChecked():
            generate_script(package,
                            host_system,
                            target_system,
                            functions,
                            library_path,
                            library_name,
                            utils.IA32,
                            threading,
                            threading_layer_type, )
        if self.intel64.isChecked():
            generate_script(package,
                            host_system,
                            target_system,
                            functions,
                            library_path,
                            library_name,
                            utils.INTEL64,
                            threading,
                            threading_layer_type)
        QMessageBox.about(self, 'Success', 'Generation completed!')

    @interface_check
    def on_target_system_selection(self):
        system = self.platform_var_list.currentText()
        if utils.ONLY_THREADABLE[system]:
            self.thread_var_list.setCurrentIndex(1)
            self.thread_var_list.setDisabled(True)
        else:
            self.thread_var_list.setDisabled(False)

        if not utils.SUPPORTED_ARCHITECTURES[system][utils.IA32]:
            self.IA32.setCheckState(Qt.Unchecked)
            self.IA32.setDisabled(True)
        else:
            self.IA32.setDisabled(False)
        if not utils.SUPPORTED_ARCHITECTURES[system][utils.INTEL64]:
            self.intel64.setCheckState(Qt.Unchecked)
            self.intel64.setDisabled(True)
        else:
            self.intel64.setDisabled(False)

    def add_items(self, items):
        """
        Sorts and adds items to list view

        :param items: list of strings
        """
        self.functions_list.clear()

        if items:
            items.sort()
            self.functions_list.addItems(items)
            self.functions_list.setCurrentItem(self.functions_list.item(0))

        self.functions_list.repaint()

    def add_item(self, function):
        """
        Adds new function to required list

        :param domain: domain of function
        :param function: name if function
        """
        self.functions_names.append(function)
        self.add_items(self.functions_names)

    def remove_item(self):
        """
        Removes function from required list
        """
        if self.functions_list.currentItem() is None:
            return None
        lib = self.functions_list.currentItem().text()
        self.functions_names.remove(lib)
        self.add_items(self.functions_names)
        return lib

    def __post_check(self):
        """
        Fills platforms combo box according to host system
        """
        if utils.HOST_SYSTEM == utils.LINUX:
            self.platform_var_list.addItem(utils.LINUX)
        elif utils.HOST_SYSTEM == utils.MACOSX:
            self.platform_var_list.addItem(utils.MACOSX)
        elif utils.HOST_SYSTEM == utils.WINDOWS:
            self.platform_var_list.addItem(utils.WINDOWS)
Example #2
0
class CaptureWidget(QWidget):
    """
    This QWidget gather and check the user provided metadata and ask the MainWindow to launch the capture

    Attributes:
        self.receive_enable_decklink_radio_1 (pyqtSignal([bool])): signal sent by the StatusWidget
        self.receive_enable_decklink_radio_2 (pyqtSignal([bool])): signal sent by the StatusWidget
        self.set_statusbar_text_signal (pyqtSignal([str])): Is used to display text on the MainWindow's statusbar
    """

    receive_enable_decklink_radio_1 = pyqtSignal([bool])
    receive_enable_decklink_radio_2 = pyqtSignal([bool])
    set_statusbar_text_signal = pyqtSignal([str])

    def __init__(self, parent):
        # Initialize the parent class QWidget
        # this allow the use of the parent's methods when needed
        super(CaptureWidget, self).__init__(parent=parent)
        self.main_window = None

        #########
        self.decklink_label = QLabel("Choisissez la source vidéo")
        self.decklink_radio_1 = QRadioButton("Decklink 1 (NTSC/PAL)")
        self.decklink_radio_2 = QRadioButton("Decklink 2 (NTSC/PAL/SECAM)")
        self.file_import_radio = QRadioButton("importer fichier vidéo")
        self.dvd_import_radio = QRadioButton("importer dvd")
        self.lossless_import_checkbox = QCheckBox(
            "Importer sans perte de qualité")

        #########
        self.digitise_table = QTableWidget()
        self.table_font = QFont(QFont().defaultFamily(), 12)
        self.new_table_row_button = QPushButton("Ajouter")
        self.launch_ffplay_button = QPushButton("Lancer aperçu")
        self.launch_digitise_button = QPushButton("Numériser")

        #########
        dc_data = OrderedDict()
        dc_data['dc:contributor'] = "nom des acteurs"
        dc_data['dc:creator'] = "maison d'édition, scénariste ou réalisateur"
        dc_data['dc:description'] = "résumé de la vidéo"
        dc_data['dc:language'] = "langue de la vidéo: FRA, ENG"
        dc_data[
            'dc:publisher'] = "entreprise qui a publié le film, par exemple Sony Pictures"
        dc_data[
            'dc:subject'] = "thème du film: horreur, action, histoire d'amour..."
        dc_data['dc:title'] = "titre du film"
        dc_data[
            'dcterms:isPartOf'] = "remplir si le film fait partie d'un ensemble de films comme Star Wars"
        dc_data['dcterms:created'] = "année de sortie du film"
        dc_data['durée'] = "durée du film en minutes"
        dc_data['ratio'] = "format visuel du film"
        dc_data['format_video'] = "format video de la cassette"
        self.digitise_table_row_tooltip = dc_data

        #########
        self.raw_videos_path = FILES_PATHS["raw"]
        self.compressed_videos_path = FILES_PATHS["compressed"]
        self.imported_files_path = FILES_PATHS["imported"]

        #########
        self.backend_is_alive_timer = QTimer()

        #########
        self.tab_init()

    def delayed_init(self):
        """
        Is called if the wamp router is successfully joined, if you don't wait, you get this:

        'NoneType' object has no attribute 'parent'
        Traceback (most recent call last):
            File "/usr/local/lib/python3.4/dist-packages/autobahn/wamp/websocket.py", line 62, in onOpen
                self._session.onOpen(self)
        AttributeError: 'NoneType' object has no attribute 'onOpen'
        """
        self.main_window = self.parent().parent().parent(
        )  # MainWindow -> QTabWidget -> QStackedWidget

    @wrap_in_future
    @asyncio.coroutine
    def launch_capture(self, metadata):
        """
        Call the 'launch_capture' function in the backend

        Args:
            metadata (list): [digitise_infos, dublincore_dict]
        """
        print("metadata")
        print(metadata)
        yield from self.main_window.call('com.digitize_app.launch_capture',
                                         metadata)

    def launch_ffplay(self):
        decklink_id = None
        video_format = None

        if self.decklink_radio_1.isChecked(
        ) and self.decklink_radio_1.isEnabled():
            decklink_id = '1'
        elif self.decklink_radio_2.isChecked(
        ) and self.decklink_radio_2.isEnabled():
            decklink_id = '2'
        else:
            error_box = QMessageBox()
            error_box.setText(
                "Veuillez sélectionner une carte d'aquisition non utilisée")
            error_box.setWindowTitle("Erreur")
            error_box.exec_()

        for row in range(self.digitise_table.rowCount()):
            combobox_text = self.digitise_table.cellWidget(row,
                                                           0).currentText()
            if combobox_text == 'format_video':
                video_format = self.digitise_table.cellWidget(row,
                                                              1).currentText()
                break
        if video_format is None and decklink_id == '1':
            error_box = QMessageBox()
            error_box.setText("Veuillez préciser le format de la vidéo")
            error_box.setWindowTitle("Erreur")
            error_box.exec_()

        if decklink_id == '1' and video_format:
            decklink_input = "Intensity Pro (1)"

            if video_format == 'SECAM':
                error_box = QMessageBox()
                error_box.setText(
                    "Il est impossible de numériser une cassette SECAM avec la carte numéro 1"
                )
                error_box.setWindowTitle("Erreur")
                error_box.exec_()
            elif video_format == 'PAL':
                subprocess.Popen([
                    'ffplay', '-f', 'decklink', '-format_code', 'pal',
                    '-video_input', 'composite', '-i', decklink_input
                ])
            elif video_format == 'NTSC':
                subprocess.Popen([
                    'ffplay', '-f', 'decklink', '-format_code', 'ntsc',
                    '-video_input', 'composite', '-i', decklink_input
                ])
        elif decklink_id == '2':
            decklink_input = "Intensity Pro (2)"
            subprocess.Popen([
                'ffplay', '-f', 'decklink', '-format_code', 'hp60',
                '-video_input', 'hdmi', '-aspect', '4:3', '-i', decklink_input
            ])

    def backend_is_alive_beacon(self):
        """
        Is called when the backend send a beacon
        Effect: make a timer decrement from 15000 ms. If the timer reach zero, the widget can't start a new capture.
        """
        self.backend_is_alive_timer.setInterval(39000)

    def add_table_row(self):
        """
        Is called when the "self.new_table_row_button" button is pressed

        This function will fill the combobox with their name and a tooltip, link the combobox
         to the "self.combobox_changed function" and link the delete button with the self.delete_table_row" function
        """

        row_count = self.digitise_table.rowCount()
        self.digitise_table.insertRow(row_count)
        self.digitise_table.setCellWidget(row_count, 0, QComboBox())

        count = 0
        for dc_key, dc_tooltip in self.digitise_table_row_tooltip.items():
            self.digitise_table.cellWidget(row_count, 0).addItem(dc_key)
            self.digitise_table.cellWidget(row_count, 0).setItemData(
                count, dc_tooltip, Qt.ToolTipRole)
            count += 1

        self.digitise_table.cellWidget(row_count, 0).activated[str].connect(
            self.combobox_changed)
        self.digitise_table.setCellWidget(row_count, 1, QLineEdit())
        self.digitise_table.setCellWidget(row_count, 2, QPushButton("Delete"))
        self.digitise_table.cellWidget(row_count, 2).clicked.connect(
            self.delete_table_row)

    def delete_table_row(self):
        """
        Is linked to the delete button when a row is added.

        When the delete button is pressed, the function look up its row and delete it
        """

        sender = self.sender()
        index = self.digitise_table.indexAt(sender.pos())
        if index.isValid():
            self.digitise_table.removeRow(index.row())

    def combobox_changed(self, text):
        """
        Is linked to the combobox when a row is added

        When the combobox selected item changes (example: from dc:contributor to dc:description),
        this function is called to make the row fit its new usage. (example: enter text or a date)

        Args:
            text (str): its the active combobox selection
        """
        index = self.digitise_table.indexAt(self.sender().pos())

        comboboxes_names_counter = []
        for row in range(self.digitise_table.rowCount()):
            comboboxes_names_counter.append(
                self.digitise_table.cellWidget(row, 0).currentText())
        comboboxes_names_counter = Counter(comboboxes_names_counter)

        forbidden_duplicates = [
            "dc:description", "dcterms:created", "durée", "ratio",
            "format_video"
        ]

        if text in forbidden_duplicates and comboboxes_names_counter[text] > 1:
            self.digitise_table.cellWidget(index.row(),
                                           0).setCurrentText('dc:contributor')
            text = 'dc:contributor'
            error_box = QMessageBox()
            error_box.setText(
                "Il est impossible d'avoir plus d'une entrée de ce type")
            error_box.setWindowTitle("Erreur")
            error_box.exec_()

        if index.isValid():
            row = index.row()
            if text == "dc:description":
                self.digitise_table.removeCellWidget(row, 1)
                self.digitise_table.setCellWidget(row, 1, QTextEdit())
                self.digitise_table.setRowHeight(row, 60)
            elif text == "dcterms:created":
                self.digitise_table.removeCellWidget(row, 1)
                self.digitise_table.setCellWidget(row, 1, QLineEdit())
                self.digitise_table.cellWidget(row, 1).setInputMask("0000")
                self.digitise_table.setRowHeight(row, 30)
            elif text == "durée":
                self.digitise_table.removeCellWidget(row, 1)
                self.digitise_table.setCellWidget(row, 1, QLineEdit())
                self.digitise_table.setRowHeight(row, 30)
                self.digitise_table.cellWidget(row, 1).setInputMask("000")
            elif text == "ratio":
                self.digitise_table.removeCellWidget(row, 1)
                self.digitise_table.setCellWidget(row, 1, QComboBox())
                self.digitise_table.setRowHeight(row, 30)
                self.digitise_table.cellWidget(row,
                                               1).addItems(["4:3", "16:9"])
            elif text == "format_video":
                self.digitise_table.removeCellWidget(row, 1)
                self.digitise_table.setCellWidget(row, 1, QComboBox())
                self.digitise_table.setRowHeight(row, 30)
                self.digitise_table.cellWidget(row, 1).addItems(
                    ["PAL", "SECAM", "NTSC"])
            elif text == "dc:language":
                self.digitise_table.removeCellWidget(row, 1)
                self.digitise_table.setCellWidget(row, 1, QLineEdit())
                self.digitise_table.setRowHeight(row, 30)
                self.digitise_table.cellWidget(row, 1).setInputMask("AAA")
            else:
                self.digitise_table.removeCellWidget(row, 1)
                self.digitise_table.setCellWidget(row, 1, QLineEdit())
                self.digitise_table.setRowHeight(row, 30)

    def metadata_checker(self, capture_action, data):
        """
        Check if the required metadata is present. If yes it launches the launch_capture function

        Args:
            capture_action (str): tell which capture_action the metadata_checker function should launch
                Possible values: decklink, file, DVD
            data: [digitise_infos, dublincore_dict]
        """

        # this check if at least a duration, title, and creation date is set before sending the data to the back end
        if capture_action == "decklink" and "duration" in data[1].get('dc:format', {}) \
                and "format" in data[1].get('dc:format', {}) and "dc:title" in data[1] and "dcterms:created" in data[1] \
                and self.check_remaining_space(VHS_duration=data[1]["dc:format"]["duration"]):

            self.launch_digitise_button.setEnabled(False)

            self.launch_capture(data)
            self.launch_digitise_button.setEnabled(True)
            # set status bar temp text
            self.set_statusbar_text_signal.emit("Numérisation Decklink lancée")

        elif capture_action == "file" and "file_path" in data[0] and "dc:title" in data[1] and "dcterms:created" in data[1] \
                and self.check_remaining_space(import_file_path=data[0]["file_path"]):

            self.launch_digitise_button.setEnabled(False)

            self.launch_capture(data)
            self.launch_digitise_button.setEnabled(True)

            self.set_statusbar_text_signal.emit(
                "Enregistrement du fichier lancé !")

        elif capture_action == "DVD" and "dc:title" in data[1] and "dcterms:created" in data[1] \
                and self.check_remaining_space(for_DVD=True):

            self.launch_digitise_button.setEnabled(False)

            self.launch_capture(data)
            self.launch_digitise_button.setEnabled(True)

            self.set_statusbar_text_signal.emit(
                "Enregistrement du DVD lancé !")
        else:
            warning_box = QMessageBox()
            warning_message = (
                "Les informations suivantes sont necessaires:\n"
                "   Pour enregistrer un dvd:\n"
                "       un titre et la date de creation de l'oeuvre\n"
                "   Pour enregistrer une cassette:\n"
                "       la durée, un titre, le format et l'année de creation de l'oeuvre\n"
                "   Pour copier un fichier:\n"
                "       un titre et la date de création de l'oeuvre\n"
                "\n"
                "   Il faut aussi avoir sélectionné une méthode d'enregistrement (decklink, dvd...)"
            )

            warning_box.warning(warning_box, "Attention", warning_message)
            self.launch_digitise_button.setEnabled(True)

    def check_remaining_space(self,
                              for_DVD=None,
                              import_file_path=None,
                              VHS_duration=None):
        """
        Check the remaining space in the folder where the video will be saved

        Args:
            for_DVD (bool):
            import_file_path (str):
            VHS_duration (str):

        Returns:
            bool: True if successful, False otherwise.
        """

        error_text = "L'espace disque est insuffisant pour enregistrer la vidéo, " + \
                     "veuillez contacter le responsable informatique."

        if for_DVD:
            free_space = shutil.disk_usage(self.compressed_videos_path)[2]
            file_size = 15000000000  # 15GB
            if free_space - file_size < 10000000000:  # 10GB
                error_box = QMessageBox()
                error_box.setText(error_text)
                error_box.setWindowTitle("Erreur")
                error_box.exec_()
                return False
            else:
                return True
        elif import_file_path:
            free_space = shutil.disk_usage(self.imported_files_path)[2]
            file_size = os.path.getsize(import_file_path)
            if free_space - file_size < 10000000000:  # 10GB
                error_box = QMessageBox()
                error_box.setText(error_text)
                error_box.setWindowTitle("Erreur")
                error_box.exec_()
                return False
            else:
                return True
        elif VHS_duration:
            free_space = shutil.disk_usage(self.compressed_videos_path)[2]
            file_size = VHS_duration * 6.6 * 1000000000
            if free_space - file_size < 100000000000:  # 100GB
                error_box = QMessageBox()
                error_box.setText(error_text)
                error_box.setWindowTitle("Erreur")
                error_box.exec_()
                return False
            else:
                return True

    def gather_metadata(self):
        """
        Gather the user provided metadata and add the constants listed below.
            dublincore_dict["dc:rights"] = "usage libre pour l'éducation"
            dublincore_dict["dc:type"] = "video"
            dublincore_dict["dcterms:modified"] = datetime.now().replace(microsecond=0).isoformat()

        Notes:
            This function also set default values for this key but it can be overridden by the user
            dublincore_dict['dc:format'] = {'aspect_ratio': '4:3', 'format': 'Non spécifié'}

        Call the 'self.metadata_checker' function with the parameter [digitise_infos, dublincore_dict]
        """

        # prevent button hammering
        self.launch_digitise_button.setEnabled(False)

        file_path = None
        if self.file_import_radio.isChecked():
            file_dialog = QFileDialog(self)
            file_path = file_dialog.getOpenFileName(
                directory=FILES_PATHS["home_dir"])
            file_path = file_path[0]
            print(file_path)

        dublincore_dict = dict()
        dublincore_dict["dc:format"] = {"aspect_ratio": "4:3"}

        for row in range(self.digitise_table.rowCount()):
            combobox_text = self.digitise_table.cellWidget(row,
                                                           0).currentText()
            widget_type = self.digitise_table.cellWidget(
                row, 1).metaObject().className()
            if widget_type == "QLineEdit":
                widget_text_value = self.digitise_table.cellWidget(
                    row, 1).displayText()
            elif widget_type == "QTextEdit":
                widget_text_value = self.digitise_table.cellWidget(
                    row, 1).toPlainText()
            elif widget_type == "QComboBox":
                widget_text_value = self.digitise_table.cellWidget(
                    row, 1).currentText()

            if widget_text_value != "":
                if combobox_text == "durée":
                    dublincore_dict["dc:format"]["duration"] = int(
                        widget_text_value) * 60  # convert minutes to seconds
                elif combobox_text == "ratio":
                    dublincore_dict["dc:format"][
                        "aspect_ratio"] = widget_text_value
                elif combobox_text == "format_video":
                    dublincore_dict["dc:format"]["format"] = widget_text_value
                elif combobox_text == "dcterms:created":
                    dublincore_dict[combobox_text] = int(widget_text_value)
                elif combobox_text == "dc:description":
                    dublincore_dict[combobox_text] = widget_text_value
                else:
                    try:
                        dublincore_dict[combobox_text].append(
                            widget_text_value)
                    except KeyError:
                        dublincore_dict[combobox_text] = [widget_text_value]
        dublincore_dict["dc:rights"] = "usage libre pour l'éducation"
        dublincore_dict["dc:type"] = "video"
        dublincore_dict["dcterms:modified"] = datetime.now().replace(
            microsecond=0).isoformat()

        # Handle the other infos
        capture_action = None
        digitise_infos = {}
        if self.decklink_radio_1.isChecked(
        ) and self.decklink_radio_1.isEnabled():
            digitise_infos["source"] = "decklink_1"
            digitise_infos[
                "lossless_import"] = self.lossless_import_checkbox.isChecked()
            capture_action = "decklink"
        elif self.decklink_radio_2.isChecked(
        ) and self.decklink_radio_2.isEnabled():
            digitise_infos["source"] = "decklink_2"
            digitise_infos[
                "lossless_import"] = self.lossless_import_checkbox.isChecked()
            capture_action = "decklink"
        elif self.file_import_radio.isChecked():
            digitise_infos["source"] = "file"
            capture_action = "file"
        elif self.dvd_import_radio.isChecked():
            digitise_infos["source"] = "DVD"
            capture_action = "DVD"

        digitise_infos["file_path"] = file_path

        to_be_send = [digitise_infos, dublincore_dict]
        print(to_be_send)

        self.metadata_checker(capture_action=capture_action, data=to_be_send)

    def tab_init(self):
        """
        Is called when the CaptureWidget class init

        Its job is to put the widgets instantiated in the init function to their place and set some signals between
         functions and buttons
        """

        grid = QGridLayout()
        self.setLayout(grid)

        #########
        self.digitise_table.setRowCount(0)
        self.digitise_table.setColumnCount(3)
        self.digitise_table.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.Stretch)
        self.digitise_table.setColumnWidth(0, 170)
        self.digitise_table.setFont(self.table_font)
        self.digitise_table.setHorizontalHeaderLabels(["", "", ""])

        #########
        grid.addWidget(self.decklink_label, 0, 0)
        grid.addWidget(self.decklink_radio_1, 0, 1)
        grid.addWidget(self.file_import_radio, 0, 3)
        grid.addWidget(self.lossless_import_checkbox, 1, 0)
        grid.addWidget(self.decklink_radio_2, 1, 1)
        grid.addWidget(self.dvd_import_radio, 1, 3)

        grid.addWidget(self.digitise_table, 2, 0, 7, 4)
        grid.addWidget(self.new_table_row_button, 2, 5)
        grid.addWidget(self.launch_ffplay_button, 5, 5)
        grid.addWidget(self.launch_digitise_button, 8, 5)

        #########
        self.dvd_import_radio.toggled.connect(
            self.lossless_import_checkbox.setDisabled)
        self.dvd_import_radio.toggled.connect(
            self.launch_ffplay_button.setDisabled)
        self.file_import_radio.toggled.connect(
            self.lossless_import_checkbox.setDisabled)
        self.file_import_radio.toggled.connect(
            self.launch_ffplay_button.setDisabled)

        #########
        self.backend_is_alive_timer.start(15000)
        self.backend_is_alive_timer.timeout.connect(
            partial(self.launch_digitise_button.setDisabled, True))
        self.receive_enable_decklink_radio_1.connect(
            self.decklink_radio_1.setEnabled)
        self.receive_enable_decklink_radio_2.connect(
            self.decklink_radio_2.setEnabled)

        #########
        self.new_table_row_button.clicked.connect(self.add_table_row)
        self.launch_ffplay_button.clicked.connect(self.launch_ffplay)
        self.launch_digitise_button.clicked.connect(self.gather_metadata)
Example #3
0
class ConditionDialog(QDialog):
    def __init__(self, expr_pool):
        super().__init__()
        self.expr_pool = expr_pool
        self.stack = QStackedLayout()
        self.stack.addWidget(self.build_first_page())
        self.stack.addWidget(self.build_second_page())
        self.stack.addWidget(self.build_third_page())
        self.setLayout(self.stack)
        self.setWindowTitle('Add condition')

    def build_first_page(self):
        first_page = QWidget()
        expression_type_box = QGroupBox('Select condition type')
        self.simple_button = QRadioButton(
            'Simple condition\nUse an expression, a comparator and a threshold value'
            'to create a new condition. Example: B > 0')
        self.simple_button.setChecked(True)
        self.and_or_button = QRadioButton(
            'AND/OR condition\nUse two existing conditions and AND/OR operators'
            'to create a new condition. Example: (B > 0) AND (B < 100)')
        self.and_or_button.setEnabled(self.expr_pool.nb_conditions() > 1)
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.simple_button)
        vlayout.addWidget(self.and_or_button)
        expression_type_box.setLayout(vlayout)
        next_button = QPushButton('Next')
        cancel_button = QPushButton('Cancel')
        for bt in (next_button, cancel_button):
            bt.setMaximumWidth(200)
            bt.setFixedHeight(30)
        hlayout = QHBoxLayout()
        hlayout.addStretch()
        hlayout.addWidget(next_button)
        hlayout.addWidget(cancel_button)
        vlayout = QVBoxLayout()
        vlayout.addWidget(expression_type_box)
        vlayout.addStretch()
        vlayout.addLayout(hlayout, Qt.AlignRight)
        first_page.setLayout(vlayout)

        next_button.clicked.connect(self.turn_page)
        cancel_button.clicked.connect(self.reject)
        return first_page

    def build_second_page(self):
        second_page = QWidget()
        buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        buttons.accepted.connect(self.check)
        buttons.rejected.connect(self.reject)

        self.expression_box = QComboBox()
        self.expression_box.setFixedHeight(30)
        self.expression_box.setMinimumWidth(150)
        self.expression_box.setMaximumWidth(250)

        for i in range(1, self.expr_pool.nb_expressions() + 1):
            expr = self.expr_pool.expressions()[i]
            if expr.masked:
                continue
            self.expression_box.addItem(str(expr))

        self.comparator_box = QComboBox()
        for comparator in ['>', '<', '>=', '<=']:
            self.comparator_box.addItem(comparator)
        self.comparator_box.setFixedSize(50, 30)

        self.threshold_box = QLineEdit()
        self.threshold_box.setFixedSize(150, 30)

        vlayout = QVBoxLayout()
        glayout = QGridLayout()
        glayout.addWidget(QLabel('Expression'), 1, 1, Qt.AlignHCenter)
        glayout.addWidget(QLabel('Comparator'), 1, 2, Qt.AlignHCenter)
        glayout.addWidget(QLabel('Threshold'), 1, 3, Qt.AlignHCenter)
        glayout.addWidget(self.expression_box, 2, 1)
        glayout.addWidget(self.comparator_box, 2, 2)
        glayout.addWidget(self.threshold_box, 2, 3)
        glayout.setVerticalSpacing(12)
        glayout.setRowStretch(0, 1)
        vlayout.addLayout(glayout)
        vlayout.addStretch()
        vlayout.addWidget(buttons)
        second_page.setLayout(vlayout)
        return second_page

    def build_third_page(self):
        third_page = QWidget()
        if not self.and_or_button.isEnabled():
            return third_page
        self.and_or_box = QComboBox()
        self.and_or_box.addItem('AND')
        self.and_or_box.addItem('OR')
        self.first_box = QComboBox()
        self.second_box = QComboBox()
        ok_button = QPushButton('OK')
        cancel_button = QPushButton('Cancel')
        for bt in (ok_button, cancel_button):
            bt.setMaximumWidth(200)
            bt.setFixedHeight(30)
        for box in (self.first_box, self.second_box):
            box.setFixedHeight(30)
            box.setMaximumWidth(250)
        self.and_or_box.setFixedSize(100, 30)
        for i in range(1, self.expr_pool.nb_conditions() + 1):
            condition = str(self.expr_pool.conditions()[i])
            self.first_box.addItem(condition)
            self.second_box.addItem(condition)
        vlayout = QVBoxLayout()
        glayout = QGridLayout()
        glayout.addWidget(QLabel('Condition 1'), 1, 1, Qt.AlignHCenter)
        glayout.addWidget(QLabel('Operator'), 1, 2, Qt.AlignHCenter)
        glayout.addWidget(QLabel('Condition 2'), 1, 3, Qt.AlignHCenter)
        glayout.addWidget(self.first_box, 2, 1)
        glayout.addWidget(self.and_or_box, 2, 2)
        glayout.addWidget(self.second_box, 2, 3)
        glayout.setVerticalSpacing(12)
        glayout.setRowStretch(0, 1)
        vlayout.addLayout(glayout)
        vlayout.addStretch()
        hlayout = QHBoxLayout()
        hlayout.addStretch()
        hlayout.addWidget(ok_button)
        hlayout.addWidget(cancel_button)
        vlayout.addLayout(hlayout)
        third_page.setLayout(vlayout)

        ok_button.clicked.connect(self.check)
        cancel_button.clicked.connect(self.reject)
        return third_page

    def turn_page(self):
        if self.simple_button.isChecked():
            self.stack.setCurrentIndex(1)
        else:
            self.stack.setCurrentIndex(2)

    def check(self):
        current_page = self.stack.currentIndex()
        if current_page == 1:
            threshold = self.threshold_box.text()
            try:
                threshold = float(threshold)
            except ValueError:
                QMessageBox.critical(None, 'Error',
                                     'The threshold is not a number!',
                                     QMessageBox.Ok)
                return
            expr_text = self.expression_box.currentText()
            self.expr_pool.add_condition(
                self.expr_pool.get_expression(expr_text),
                self.comparator_box.currentText(), threshold)
        else:
            is_and = self.and_or_box.currentText() == 'AND'
            first_text, second_text = self.first_box.currentText(
            ), self.second_box.currentText()
            if first_text == second_text:
                QMessageBox.critical(
                    None, 'Error', 'The two conditions cannot be identical!',
                    QMessageBox.Ok)
                return
            success_code = self.expr_pool.add_and_or_condition(
                self.expr_pool.get_condition(first_text),
                self.expr_pool.get_condition(second_text), is_and)
            if success_code == -2:
                QMessageBox.critical(
                    None, 'Error',
                    'One condition can only use only one polygonal mask!',
                    QMessageBox.Ok)
                return
        self.accept()
Example #4
0
class FunctionsMenu(QWidget):
    def __init__(self):
        super().__init__()
        self.layout = QGridLayout()
        self.setLayout(self.layout)

        row_hight = 30
        margins = self.layout.contentsMargins()
        margins.setTop(2*row_hight + self.layout.spacing())
        margins.setLeft(20)
        self.layout.setContentsMargins(margins)

        # Initializing GUI elements
        self.ipp = QRadioButton()
        self.ippcp = QRadioButton()

        self.select_ipp = QPushButton('Select package')
        self.select_ippcp = QPushButton('Select package')
        self.select_ipp.package = utils.IPP
        self.select_ippcp.package = utils.IPPCP

        self.lib_var_list = QComboBox(self)
        self.search = QLineEdit(self)
        self.selected_libraries_list = QListWidget(self)
        self.auto_build_button = QPushButton('Autobuild')

        self.current_functions_list = {}

        # Preparing elements by giving initial values, etc
        self.ipp.setText('IPP ver. None')
        self.ippcp.setText('IPP Cryptography ver. None')
        self.search.setPlaceholderText('Search...')

        self.ipp.setDisabled(True)
        self.ippcp.setDisabled(True)
        self.auto_build_button.setDisabled(True)

        self.lib_var_list.activated[str].connect(self.on_theme_selected)
        self.search.textEdited.connect(self.on_search)
        self.selected_libraries_list.itemClicked.connect(self.on_item_selected)
        self.auto_build_button.clicked.connect(self.on_build_pressed)
        self.select_ipp.clicked.connect(self.on_select)
        self.select_ippcp.clicked.connect(self.on_select)
        self.ipp.toggled.connect(self.on_switch)

        # Setting all widgets in their places
        self.layout.addWidget(self.ipp, 0, 0, 1, 3)
        self.layout.addWidget(self.ippcp, 1, 0, 1, 3)

        self.layout.addWidget(self.select_ipp, 0, 3)
        self.layout.addWidget(self.select_ippcp, 1, 3)

        self.layout.addWidget(self.lib_var_list, 2, 0, 1, 4)
        self.layout.addWidget(self.search, 3, 0, 1, 4)
        self.layout.addWidget(self.selected_libraries_list, 4, 0, 1, 4)
        self.layout.addWidget(self.auto_build_button, 5, 0, 1, 4)

        self.select_ipp.setFixedHeight(row_hight)
        self.select_ippcp.setFixedHeight(row_hight)
        self.lib_var_list.setFixedHeight(row_hight)

        path_to_package = self.get_path_to_package()
        if path_to_package:
            package = utils.IPPCP if re.compile('.*ippcp.*').match(path_to_package) else utils.IPP
            self.set_package(package, path_to_package)
            self.init_menu()

    def init_menu(self):
        self.ipp.toggled.disconnect(self.on_switch)
        self.tl_selected = False

        if self.ipp.isEnabled():
            package = utils.IPP
            self.init_functions(utils.IPP, settings.CONFIGS[utils.IPP]['Path'])
            self.ipp.setChecked(True)

        if self.ippcp.isEnabled():
            self.init_functions(utils.IPPCP, settings.CONFIGS[utils.IPPCP]['Path'])
            if not self.ipp.isEnabled():
                package = utils.IPPCP
                self.ippcp.setChecked(True)

        self.show_menu(package)
        self.ipp.toggled.connect(self.on_switch)

    def set_package(self, package, path):
        if self.check_package(package, path):
            settings.CONFIGS[package]['Path'] = path
            if package == utils.IPP:
                self.ipp.setText('IPP ver. ' + self.get_version(path))
                self.ipp.setEnabled(True)
            else:
                self.ippcp.setText('IPP Cryptography ver. ' + self.get_version(path))
                self.ippcp.setEnabled(True)
            return True
        elif path:
            if package == utils.IPP:
                QMessageBox.information(self, 'ERROR!',
                                        'Incorrect Intel® Integrated Performance Primitives package path!')
            else:
                QMessageBox.information(self, 'ERROR!',
                                        'Incorrect Intel® Integrated Performance Primitives Cryptography package path!')

            return False

    def check_package(self, package, path):
        if not self.check_batch(package, path):
            return False

        if os.path.exists(os.path.join(path, 'include')):
            for header in os.listdir(os.path.join(path, 'include')):
                if get_mode_and_domain(header):
                    break
        else:
            return False

        lib_ia32_path = os.path.join(path, 'lib', 'ia32_' + utils.HOST_SYSTEM.lower()[:3])
        if utils.HOST_SYSTEM != MACOSX:
            lib_intel64_path = os.path.join(path, 'lib', 'intel64_' + utils.HOST_SYSTEM.lower()[:3])
        else:
            lib_intel64_path = os.path.join(path, 'lib')

        return True if self.check_libs(lib_ia32_path) or \
                       self.check_libs(lib_intel64_path) else False

    def check_batch(self, package, path):
        return os.path.exists(os.path.join(path, 'bin', package.lower() + 'vars' + BATCH_EXTENSIONS[utils.HOST_SYSTEM]))

    def check_libs(self, path):
        if os.path.exists(path):
            for lib in os.listdir(path):
                if STATIC_LIBRARIES_EXTENSIONS[utils.HOST_SYSTEM] in lib:
                    return True
        return False

    def on_select(self):
        package = self.sender().package
        while True:
            path = QFileDialog.getExistingDirectory(self, 'Select ' + package + ' package')
            if not path:
                return

            if self.set_package(package, path):
                break

        self.init_functions(package, path)

        if package == utils.IPP:
            if not self.ipp.isChecked() and not self.ippcp.isChecked():
                self.ipp.toggled.disconnect(self.on_switch)
                self.ipp.setChecked(True)
                self.ipp.toggled.connect(self.on_switch)
        else:
            if not self.ipp.isChecked() and not self.ippcp.isChecked():
                self.ippcp.setChecked(True)

        for config in settings.CONFIGS[package].keys():
            if config != 'Path' and config != 'functions_list':
                settings.CONFIGS[package][config] = False
        settings.CONFIGS[package]['functions_list'].clear()

        if package == utils.IPP and self.ipp.isChecked() or \
                package == utils.IPPCP and self.ippcp.isChecked():
            self.parent.receiver.set_configs(package)
            self.parent.receiver.functions_names.clear()
            self.parent.receiver.functions_list.clear()
            self.show_menu(package)

        self.parent.receiver.on_block()

    def on_switch(self):
        if self.ipp.isChecked():
            package = utils.IPP
            self.parent.receiver.get_configs(utils.IPPCP)
        else:
            package = utils.IPPCP
            self.parent.receiver.get_configs(utils.IPP)

        self.parent.receiver.set_configs(package)
        self.show_menu(package)
        self.parent.receiver.add_items(self.parent.receiver.functions_names)
        self.parent.receiver.on_block()

    def on_theme_selected(self, text):
        self.add_items(self.current_functions_list.get(text))
        self.on_search(self.search.text())

    def on_search(self, search_request):
        if self.current_functions_list:
            self.add_items([entry for entry in self.current_functions_list.get(self.lib_var_list.currentText())
                            if search_request.upper() in entry.upper()])

    def on_item_selected(self, item):
        self.selected_libraries_list.setCurrentItem(item)

    def on_build_pressed(self):
        information = self.parent.get_library_information()
        library_path = QFileDialog.getExistingDirectory(self, 'Select a folder')
        if library_path == '':
            return
        success = False
        self.parent.set_disabled(True)
        QMessageBox.information(self, 'Build', 'Building will start after this window is closed. '
                                               'Please wait until process is done.')

        package = utils.IPP if self.ipp.isChecked() else utils.IPPCP

        os.environ[package + 'ROOT'] = settings.CONFIGS[package]['Path']
        self.get_path_to_cnl(settings.CONFIGS[package]['Path'])

        if information['ia32']:
            success = tool.core.build(
                package,
                information['host_system'],
                information['target_system'],
                information['functions'],
                library_path,
                information['library_name'],
                tool.utils.IA32,
                information['threading'] == 'Multi-threaded',
                tool.utils.COMPILERS_AND_LIBRARIES_PATH,
                threading_layer_type=information['threading_layer_type']
            )
        if information['intel64']:
            success = tool.core.build(
                package,
                information['host_system'],
                information['target_system'],
                information['functions'],
                library_path,
                information['library_name'],
                tool.utils.INTEL64,
                information['threading'] == 'Multi-threaded',
                tool.utils.COMPILERS_AND_LIBRARIES_PATH,
                threading_layer_type=information['threading_layer_type']
            )
        self.parent.set_disabled(False)
        QMessageBox.information(self, 'Success' if success else 'Failure',
                                'Build completed!' if success else 'Build failed!')

    def add_items(self, items):
        """
        Sorts and adds items to list view
        :param items: list of strings
        """
        self.selected_libraries_list.clear()

        if items:
            items.sort()
            self.selected_libraries_list.addItems(items)
            self.selected_libraries_list.setCurrentItem(self.selected_libraries_list.item(0))

        self.selected_libraries_list.repaint()

    def add_item(self, function_name):
        """
        Adds function to left list

        :param function_name:
        """
        self.operation(list.append, function_name)
        self.add_items(self.current_functions_list.get(self.lib_var_list.currentText()))
        self.on_search(self.search.text())

    def remove_item(self, item):
        """
        Removes item from left list
        """
        if item is None:
            return None

        self.operation(list.remove, item)
        self.add_items(self.current_functions_list.get(self.lib_var_list.currentText()))
        self.on_search(self.search.text())

    def operation(self, action, function_name):
        for mode in FUNCTIONS_LIST.keys():
            for domain in FUNCTIONS_LIST[mode].keys():
                if function_name in FUNCTIONS_LIST[mode][domain]:
                    action(self.functions_list[mode][domain], function_name)
                    return

    def init_func_list(self):
        """
        Taking all domains and their functions
        """
        self.lib_var_list.clear()
        self.lib_var_list.addItems([entry for entry in self.current_functions_list.keys()])

    def init_functions(self, package, path):
        if package == utils.IPP:
            FUNCTIONS_LIST['Classical IPP'].clear()
            FUNCTIONS_LIST['IPP TL'].clear()
        else:
            FUNCTIONS_LIST['IPP Cryptography'].clear()

        get_functions_from_headers(path)
        self.functions_list = copy.deepcopy(FUNCTIONS_LIST)
        self.threaded_functions = [item for sublist in self.functions_list['IPP TL'].values() for item in
                                   sublist]
    def show_menu(self, package):
        if package == utils.IPP:
            if not self.tl_selected:
                self.current_functions_list = self.functions_list['Classical IPP']
            else:
                self.current_functions_list = self.functions_list['IPP TL']
        else:
            self.current_functions_list = self.functions_list['IPP Cryptography']

        self.init_func_list()
        self.add_items(self.current_functions_list.get(self.lib_var_list.currentText()))

    def get_path_to_package(self):
        dir_path = os.path.dirname(os.path.realpath(__file__))

        if re.compile(utils.PATH_TO_PACKAGE_REGULAR_EXPRESSION).match(dir_path):
            return re.match(utils.PATH_TO_PACKAGE_REGULAR_EXPRESSION, dir_path).group('path')
        else:
            return ''

    def get_version(self, path):
        if re.compile(utils.VERSION_REGULAR_EXPRESSION).match(path):
            return re.match(utils.VERSION_REGULAR_EXPRESSION, path).group('ver')
        else:
            return 'None'

    def get_path_to_cnl(self, path):
        if re.compile(utils.PATH_TO_CNL_REGULAR_EXPRESSION).match(path):
            utils.COMPILERS_AND_LIBRARIES_PATH = re.match(utils.PATH_TO_CNL_REGULAR_EXPRESSION, path).group('cnl')
Example #5
0
class ExpressionDialog(QDialog):
    def __init__(self, expr_pool):
        super().__init__()
        self.pool = expr_pool
        self.stack = QStackedLayout()
        self.stack.addWidget(self.build_first_page())
        self.stack.addWidget(self.build_second_page())
        self.stack.addWidget(self.build_third_page())
        self.stack.addWidget(self.build_fourth_page())
        self.stack.addWidget(self.build_fifth_page())
        self.setLayout(self.stack)
        self.setWindowTitle('Add expression')

    def build_first_page(self):
        first_page = QWidget()
        expression_type_box = QGroupBox('Select expression type')
        self.simple_button = QRadioButton(
            'Simple expression\nUse variables, operators, numbers and existing'
            'expressions to create a new expression. Example: B+H+(V^2)/(2*9.81)'
        )
        self.simple_button.setChecked(True)
        self.condition_button = QRadioButton(
            'Conditional expression\nUse existing conditions and expressions'
            'to create a conditional expression. '
            'Example: IF (B > 0) THEN (B) ELSE (0)')
        self.max_min_button = QRadioButton(
            'Max/Min between two expressions\nUse two existing expressions and'
            'MAX or MIN to create a new expression. Example: MAX(B, RB+0.5)')
        self.masked_button = QRadioButton(
            'Masked expression\nUse an expression containing polygonal values '
            'and a non-polygonal expression\nto create a masked expression. '
            'Example: IF (POLY1) THEN (B+POLY1) ELSE (B)')

        self.condition_button.setEnabled(
            self.pool.ready_for_conditional_expression())
        self.max_min_button.setEnabled(
            self.pool.ready_for_max_min_expression())
        self.masked_button.setEnabled(self.pool.ready_for_masked_expression())
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.simple_button)
        vlayout.addWidget(self.condition_button)
        vlayout.addWidget(self.max_min_button)
        vlayout.addWidget(self.masked_button)
        expression_type_box.setLayout(vlayout)
        next_button = QPushButton('Next')
        cancel_button = QPushButton('Cancel')
        for bt in (next_button, cancel_button):
            bt.setMaximumWidth(200)
            bt.setFixedHeight(30)
        hlayout = QHBoxLayout()
        hlayout.addStretch()
        hlayout.addWidget(next_button)
        hlayout.addWidget(cancel_button)
        vlayout = QVBoxLayout()
        vlayout.addWidget(expression_type_box)
        vlayout.addStretch()
        vlayout.addLayout(hlayout, Qt.AlignRight)
        first_page.setLayout(vlayout)

        next_button.clicked.connect(self.turn_page)
        cancel_button.clicked.connect(self.reject)
        return first_page

    def build_second_page(self):
        second_page = QWidget()
        self.expression_text = QTextEdit()
        var_list = VariableList(self.pool, self.expression_text)
        ok_button = QPushButton('OK')
        cancel_button = QPushButton('Cancel')
        for bt in (ok_button, cancel_button):
            bt.setMaximumWidth(200)
            bt.setFixedHeight(30)
        hlayout = QHBoxLayout()
        vlayout = QVBoxLayout()
        lb = QLabel('Available variables and expressions')
        vlayout.addWidget(lb)
        vlayout.addWidget(var_list)
        vlayout.setAlignment(lb, Qt.AlignHCenter)
        hlayout.addLayout(vlayout)
        vlayout = QVBoxLayout()
        vlayout.addWidget(
            QLabel(
                '<p style="font-size:10pt">'
                '<b>Help</b>: double click on the list to add variables or existing expressions.<br>'
                'You can also enter operators, parentheses and numbers.<br>'
                'Supported operators: <tt>+ - * / ^ sqrt sin cos atan</tt>.</p>'
            ))
        vlayout.addItem(QSpacerItem(10, 10))
        vlayout.addWidget(QLabel('Expression Editor'))
        vlayout.addWidget(self.expression_text)
        hlayout.addLayout(vlayout)
        hlayout.setSpacing(10)
        vlayout = QVBoxLayout()
        vlayout.addLayout(hlayout)
        hlayout = QHBoxLayout()
        hlayout.addStretch()
        hlayout.addWidget(ok_button)
        hlayout.addWidget(cancel_button)
        vlayout.addLayout(hlayout)
        second_page.setLayout(vlayout)

        ok_button.clicked.connect(self.check)
        cancel_button.clicked.connect(self.reject)
        return second_page

    def build_third_page(self):
        third_page = QWidget()
        if not self.condition_button.isEnabled():
            return third_page
        self.condition_box = QComboBox()
        self.true_box = QComboBox()
        self.false_box = QComboBox()
        ok_button = QPushButton('OK')
        cancel_button = QPushButton('Cancel')
        for bt in (ok_button, cancel_button):
            bt.setMaximumWidth(200)
            bt.setFixedHeight(30)
        for box in (self.condition_box, self.true_box, self.false_box):
            box.setFixedHeight(30)
            box.setMaximumWidth(250)
        for i in range(1, self.pool.nb_expressions() + 1):
            expr = self.pool.expressions()[i]
            if expr.masked:
                continue
            self.true_box.addItem(str(expr))
            self.false_box.addItem(str(expr))
        for i in range(1, self.pool.nb_conditions() + 1):
            self.condition_box.addItem(str(self.pool.conditions()[i]))
        vlayout = QVBoxLayout()
        glayout = QGridLayout()
        glayout.addWidget(QLabel('Condition'), 1, 1, Qt.AlignHCenter)
        glayout.addWidget(QLabel('True'), 1, 2, Qt.AlignHCenter)
        glayout.addWidget(QLabel('False'), 1, 3, Qt.AlignHCenter)
        glayout.addWidget(self.condition_box, 2, 1)
        glayout.addWidget(self.true_box, 2, 2)
        glayout.addWidget(self.false_box, 2, 3)
        glayout.setVerticalSpacing(12)
        glayout.setRowStretch(0, 1)
        vlayout.addLayout(glayout)
        vlayout.addStretch()
        hlayout = QHBoxLayout()
        hlayout.addStretch()
        hlayout.addWidget(ok_button)
        hlayout.addWidget(cancel_button)
        vlayout.addLayout(hlayout)
        third_page.setLayout(vlayout)

        ok_button.clicked.connect(self.check)
        cancel_button.clicked.connect(self.reject)
        return third_page

    def build_fourth_page(self):
        fourth_page = QWidget()
        if not self.max_min_button.isEnabled():
            return fourth_page
        self.max_min_box = QComboBox()
        self.max_min_box.addItem('MAX')
        self.max_min_box.addItem('MIN')
        self.first_box = QComboBox()
        self.second_box = QComboBox()
        ok_button = QPushButton('OK')
        cancel_button = QPushButton('Cancel')
        for bt in (ok_button, cancel_button):
            bt.setMaximumWidth(200)
            bt.setFixedHeight(30)
        for box in (self.first_box, self.second_box):
            box.setFixedHeight(30)
            box.setMaximumWidth(250)
        self.max_min_box.setFixedSize(100, 30)
        for i in range(1, self.pool.nb_expressions() + 1):
            expr = self.pool.expressions()[i]
            if expr.masked:
                continue
            self.first_box.addItem(str(expr))
            self.second_box.addItem(str(expr))
        vlayout = QVBoxLayout()
        glayout = QGridLayout()
        glayout.addWidget(QLabel('Condition'), 1, 1, Qt.AlignHCenter)
        glayout.addWidget(QLabel('True'), 1, 2, Qt.AlignHCenter)
        glayout.addWidget(QLabel('False'), 1, 3, Qt.AlignHCenter)
        glayout.addWidget(self.max_min_box, 2, 1)
        glayout.addWidget(self.first_box, 2, 2)
        glayout.addWidget(self.second_box, 2, 3)
        glayout.setVerticalSpacing(12)
        glayout.setRowStretch(0, 1)
        vlayout.addLayout(glayout)
        vlayout.addStretch()
        hlayout = QHBoxLayout()
        hlayout.addStretch()
        hlayout.addWidget(ok_button)
        hlayout.addWidget(cancel_button)
        vlayout.addLayout(hlayout)
        fourth_page.setLayout(vlayout)

        ok_button.clicked.connect(self.check)
        cancel_button.clicked.connect(self.reject)
        return fourth_page

    def build_fifth_page(self):
        fifth_page = QWidget()
        if not self.masked_button.isEnabled():
            return fifth_page
        self.poly_box = QComboBox()
        self.inside_box = QComboBox()
        self.outside_box = QComboBox()
        ok_button = QPushButton('OK')
        cancel_button = QPushButton('Cancel')
        for bt in (ok_button, cancel_button):
            bt.setMaximumWidth(200)
            bt.setFixedHeight(30)
        for box in (self.poly_box, self.inside_box, self.outside_box):
            box.setFixedHeight(30)
            box.setMaximumWidth(250)
        for i in range(1, self.pool.nb_masks() + 1):
            mask = self.pool.masks()[i]
            if mask.nb_children > 0:
                self.poly_box.addItem(mask.code())
        for i in range(1, self.pool.nb_expressions() + 1):
            expr = self.pool.expressions()[i]
            if not expr.polygonal:
                self.outside_box.addItem(str(expr))
        self.update_inside_mask(self.poly_box.currentText())
        self.poly_box.currentTextChanged.connect(self.update_inside_mask)
        vlayout = QVBoxLayout()
        glayout = QGridLayout()
        glayout.addWidget(QLabel('Mask'), 1, 1, Qt.AlignHCenter)
        glayout.addWidget(QLabel('Inside'), 1, 2, Qt.AlignHCenter)
        glayout.addWidget(QLabel('Outside'), 1, 3, Qt.AlignHCenter)
        glayout.addWidget(self.poly_box, 2, 1)
        glayout.addWidget(self.inside_box, 2, 2)
        glayout.addWidget(self.outside_box, 2, 3)
        glayout.setVerticalSpacing(12)
        glayout.setRowStretch(0, 1)
        vlayout.addLayout(glayout)
        vlayout.addStretch()
        hlayout = QHBoxLayout()
        hlayout.addStretch()
        hlayout.addWidget(ok_button)
        hlayout.addWidget(cancel_button)
        vlayout.addLayout(hlayout)
        fifth_page.setLayout(vlayout)

        ok_button.clicked.connect(self.check)
        cancel_button.clicked.connect(self.reject)
        return fifth_page

    def update_inside_mask(self, current_mask):
        mask = self.pool.get_mask(current_mask)
        self.inside_box.clear()
        for child_code in mask.children:
            expr = self.pool.get_expression(child_code)
            self.inside_box.addItem(str(expr))

    def turn_page(self):
        if self.simple_button.isChecked():
            self.stack.setCurrentIndex(1)
        elif self.condition_button.isChecked():
            self.stack.setCurrentIndex(2)
        elif self.max_min_button.isChecked():
            self.stack.setCurrentIndex(3)
        else:
            self.stack.setCurrentIndex(4)

    def polygonal_success_message(self):
        QMessageBox.information(
            None, 'Polygonal expression created',
            'You just created an expression containing polygon values.\n'
            'To use it, click "Add Expression" then choose "Masked expression"\n'
            '(You will also need at least one non-polygonal expression).',
            QMessageBox.Ok)

    def polygonal_fail_message(self):
        QMessageBox.critical(
            None, 'Error',
            'One expression can only use only one polygonal mask!',
            QMessageBox.Ok)

    def check(self):
        current_page = self.stack.currentIndex()
        if current_page == 1:
            literal_expression = self.expression_text.toPlainText()
            success_code = self.pool.add_simple_expression(literal_expression)
            if success_code == -1:
                QMessageBox.critical(None, 'Error', 'Invalid expression.',
                                     QMessageBox.Ok)
                return
            elif success_code == -2:
                self.polygonal_fail_message()
                return
            elif success_code == 1:
                self.polygonal_success_message()

        elif current_page == 2:
            str_true, str_false = self.true_box.currentText(
            ), self.false_box.currentText()
            if str_true == str_false:
                QMessageBox.critical(
                    None, 'Error',
                    'The True/False expressions cannot be identical!',
                    QMessageBox.Ok)
                return
            str_cond = self.condition_box.currentText()
            success_code = self.pool.add_conditional_expression(
                self.pool.get_condition(str_cond),
                self.pool.get_expression(str_true),
                self.pool.get_expression(str_false))
            if success_code == -2:
                self.polygonal_fail_message()
                return
            elif success_code == 1:
                self.polygonal_success_message()
        elif current_page == 3:
            is_max = self.max_min_box.currentText() == 'MAX'
            str_first, str_second = self.first_box.currentText(
            ), self.second_box.currentText()
            if str_first == str_second:
                QMessageBox.critical(
                    None, 'Error', 'The two expressions cannot be identical!',
                    QMessageBox.Ok)
                return
            success_code = self.pool.add_max_min_expression(
                self.pool.get_expression(str_first),
                self.pool.get_expression(str_second), is_max)
            if success_code == -2:
                self.polygonal_fail_message()
                return
            elif success_code == 1:
                self.polygonal_success_message()
        else:
            str_inside, str_outside = self.inside_box.currentText(
            ), self.outside_box.currentText()
            self.pool.add_masked_expression(
                self.pool.get_expression(str_inside),
                self.pool.get_expression(str_outside))
        self.accept()