Esempio n. 1
0
class DialSetupTab(QtWidgets.QDialog, Ui_DialogSetupProject):
    header1, header2 = range(2)

    def __init__(self):
        super(DialSetupTab, self).__init__()
        self.setupUi(self)

        self.settings = Setup()
        self.data = TableProgram()

        # All Tab setup, options are split inside many function
        # Tab Project setup ---------------------------------------------------
        #   Defined data needed -----------------------------------------------
        self.list_levels = QtGui.QStandardItemModel()

        #   Write all Slot and Connect ----------------------------------------
        self.field_setup()
        self.tab_project_setup()

        # Tab Network Setup ---------------------------------------------------
        # self.tab_network()

        # Tab Source Control Setup --------------------------------------------
        self.tab_source_control()

        # Setups Buttons
        box_btn = QtWidgets.QDialogButtonBox
        btn = self.buttonBox.button
        btn(box_btn.RestoreDefaults).clicked.connect(self.btn_restore)
        btn(box_btn.Save).clicked.connect(self.save_tab)
        btn(box_btn.Open).clicked.connect(load_generic)
        btn(box_btn.Close).clicked.connect(self.close)

        # TODO Disable the Network tab settings
        self.tabWidget.setTabEnabled(2, False)

    # Ui Functions ------------------------------------------------------------
    #   Tab Project setup -----------------------------------------------------
    def tab_project_setup(self, index=None, value=str):
        """
        Generate the Tab Setup, include the Paths field and the Tree Levels
        with all editable data.
        It's only a function to add the slot and signal inside the Ui.

        :param index: None by default, this value give a Int to choice the
        field used, Unreal Editor (1) or the Project field (2).
        :param value: String data, it a simple information to send it a field
        :return:
        """
        if index:
            if index == 1:
                self.ue4_path_text.setText(value)
            elif index == 2:
                self.project_file_text.setText(value)
            elif index == 3:
                value = self.sub_folder_text.text()
                self.sub_folder_text.setText(value)
        elif self.settings.last_job_run():
            db = self.data.select_paths()
            self.ue4_path_text.setText(db[0][1])
            self.project_file_text.setText(db[0][2])
            self.sub_folder_text.setText(db[0][3])

        level_path = join(dirname(self.project_file_text.text()),
                          'Content',
                          self.sub_folder_text.text())

        root_model = self.model_base(self)
        self.ProjectTreeLevels.reset()
        self.ProjectTreeLevels.setModel(root_model)
        if self.project_file_text.text():
            data_tree = self.levels_list(level_path)
            self.model_populate(data_tree,
                                root_model.invisibleRootItem())
        self.ProjectTreeLevels.expandAll()
        self.ProjectTreeLevels.setColumnWidth(0, 250)
        self.ProjectTreeLevels.setSortingEnabled(True)
        self.ProjectTreeLevels.sortByColumn(0, Qt.AscendingOrder)
        root_model.itemChanged.connect(self.save_level)

        return self

    def field_setup(self):
        """Generate the fields and signal about all paths"""
        self.ue4_path_edit.clicked.connect(lambda: self.select_file(1))
        self.project_file_edit.clicked.connect(lambda: self.select_file(2))
        self.sub_folder_edit.clicked.connect(
            lambda: self.tab_project_setup(index=3))

    def save_field(self):
        self.data.write_data_path(self.ue4_path_text,
                                  self.project_file_text,
                                  self.sub_folder_text)

    def levels_list(self, path):
        """
        Generate a list with all levels inside a path give than argument
        :param path: specify a path to scan the folder, it's a simple string
        :return: a dict with all levels and folder
        """
        folders = {}
        levels = []
        if isdir(path):
            obj_path = PureWindowsPath(path)
            tree = listdir(str(obj_path))
            tree.sort(key=lambda s: s.find('.umap'))
            for item in tree:
                abs_path = obj_path.joinpath(item)
                if isdir(abs_path):
                    key = obj_path.stem
                    sub_levels = self.levels_list(abs_path)
                    if len(sub_levels) and type(sub_levels) == dict:
                        levels.append(sub_levels)
                        folders[key] = levels
                else:
                    if '.umap' in item:
                        regex = r"^.*Content"
                        relative_path = re.sub(regex, "", str(obj_path))
                        levels.append(join(relative_path, item))
                        key = basename(dirname(abs_path))
                        folders[key] = levels
        else:
            folders = {'No Data': 'Error Path'}

        return folders

    def save_level(self, index_item):
        """
        Function to save or remove the levels from the Data Base
        :param index_item: A string with the level name to save it.
        :return:
        """
        state = index_item.checkState()
        row = index_item.row()
        parent = index_item.parent()
        path_level = parent.child(row, 1)
        if state == Qt.Checked:
            info = [index_item.text(), path_level.text(), 1]
        else:
            info = [index_item.text(), path_level.text(), 0]

        self.data.write_data_levels(state=state, data=info)

    def model_populate(self, children, parent):
        """
        Function to work with the Model.
        You need to give 2 parameter, a dict with your Data you want show,
        and a parent to define the index.
        It's a recursive function, if your Data has a Dict inside a Dict,
        the function generate the Tree with all sub-node.
        :param children: It's only a Dict, included your Data
        :param parent: Define your first level, work with the Invisible Root
        :return: nothing returns.
        """
        for key, values in sorted(children.items()):
            item_object = QStandardItem(key)
            folder_icon = QtGui.QIcon()
            folder_icon.addPixmap(
                QtGui.QPixmap("Resources/Icons/file-submodule.png"))
            item_object.setIcon(folder_icon)
            parent.appendRow(item_object)

            if type(values) == list:
                for value in values:
                    if type(value) == str:
                        level_name = basename(value)
                        check = False
                        if self.settings.last_job_run():
                            if len(self.data.select_levels(name=level_name)):
                                check = 2
                        item_name = QStandardItem(level_name)
                        item_name.setCheckable(True)
                        item_name.setCheckState(check)
                        item_path = QStandardItem(value)
                        item_object.appendRow([item_name, item_path])
                    elif type(value) == dict:
                        self.model_populate(value, item_object)

    def model_base(self, parent):
        """
        Function to work with the Model.
        This function generate the Tree View base, with all header generate.
        :param parent: QTreeView
        :return: give a Model
        """
        model = QStandardItemModel(0, 2, parent)
        model.setHeaderData(self.header1, Qt.Horizontal, 'Names')
        model.setHeaderData(self.header2, Qt.Horizontal, 'Paths')

        return model

    def select_file(self, index):
        """Function to choose a path about the Editor and Project file"""
        select = file_open(self, index)
        self.tab_project_setup(index, select[0])

    # Ui Functions ------------------------------------------------------------
    #   Tab Source Control ----------------------------------------------------
    def tab_source_control(self):
        """
        The Ui about the Source Control panel, all option and slot connect.
        :return:
        """
        soft_work = self.softwares_comboBox
        soft_work.currentIndexChanged.connect(self.sc_software)

        if self.settings.last_job_run():
            data_sc = self.data.select_scv()
            index_cb = self.softwares_comboBox.findText(data_sc[0])
            self.softwares_comboBox.setCurrentIndex(index_cb)
            self.user_text.setText(data_sc[1])
            self.password_text.setText(data_sc[2])

    def sc_software(self):
        """
        Event on the Source control tab, activate or disable all fields about
        the option on this tab.
        Hide the password field.
        :return:
        """
        self.password_text.setEchoMode(QLineEdit.Password)

        type_items = QLineEdit, QLabel, QPushButton
        parent_setup = self.sc_groupBox_setup.findChildren(type_items)
        state = False

        if self.softwares_comboBox.currentText() == 'Disabled':
            state = True

        for item in parent_setup:
            item.setDisabled(state)

    def sc_save(self):
        sc_data = [
            self.softwares_comboBox.currentText(),
            self.user_text.text(),
            self.password_text.text()
        ]

        self.data.write_scv(sc_data)

    # Buttons Box Function ----------------------------------------------------
    @staticmethod
    def btn_restore():
        """
        Function to restore the view.
        :return:
        """
        return print('Restore View')

    def save_tab(self):
        """
        Simple function to save a new project or update the Data Base
        :return:
        """
        if not self.settings.last_job_run():
            file_save_project(self)

        else:
            # TODO Look to save the path field with the function save_field()
            # self.save_field()
            self.sc_save()
            self.data.close()
Esempio n. 2
0
class MainWindows(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        """
        Main Window, principal view, this windows can show all level,
        access on many option -path setup, network, log...
        """
        super(MainWindows, self).__init__(parent)
        self.setupUi(self)
        # Setup settings base

        # Generate all variable needed with this window
        self.settings = Setup()
        self.data = TableProgram()
        self.job = self.settings.last_job_run()

        if self.job:
            self.scv_data = self.data.select_scv()

        self.checkBoxLevels = {}

        self.menu_setup()
        self.levels_tools()
        self.levels_generate()
        self.bottom_tools()

    # Ui Function -------------------------------------------------------------
    #   File Menu setup -------------------------------------------------------
    def menu_setup(self):
        """
        Function to setup all connection an signal inside the File Menu bar.
        :return:
        """
        # File Menu
        self.actionNew_Setup.triggered.connect(self.dial_setup_project)
        open_project = self.actionLoad_Lastproject
        open_project.triggered.connect(
            lambda: load_generic(self, 'Open Project', '*.db'))

        # Setup Menu
        self.actionProject.triggered.connect(self.dial_setup_project)
        self.actionNetworks.triggered.connect(
            lambda: self.dial_setup_project(2))
        self.actionCSV.triggered.connect(lambda: self.dial_setup_project(1))

        # Log Menu
        self.actionShow_log_folder.triggered.connect(self.dialogue_log)
        self.actionClean_Log.triggered.connect(self.dialogue_log)

        # Help Menu
        self.actionAbout.triggered.connect(self.dialogue_help)
        self.actionShortcut.triggered.connect(lambda: self.dialogue_help(1))

    # Ui Function -------------------------------------------------------------
    #   Toolbars and function about the levels --------------------------------
    def levels_tools(self):
        """
        Function to setup the tools about the levels, add all signal used.
        :return:
        """
        self.pushLevelsSelect.clicked.connect(lambda: self.select_level(2))
        self.pushLevelsDeselect.clicked.connect(self.select_level)
        self.toolLevelsEdit.clicked.connect(self.dial_setup_project)

    def levels_generate(self):
        """
        Function to draw all levels setup with this project.
        :return:
        """
        group_parent = self.allLevelsWidget
        vertical_parent = self.allLevelsCheck
        vertical_parent.setAlignment(Qt.AlignLeft)

        # Generate all Checkbox Levels.
        if self.job:
            print('Make all levels Label')
            levels = self.data.select_levels()
            project_path = self.data.select_paths()
            project_path = join(dirname(project_path[0][2]) + '/Content/')
            project_path = PureWindowsPath(project_path)
            sc_software = self.scv_data[0]
            for level in levels:
                print('Loop about a level :', level)
                # Define horizontal layout
                h_layout = QtWidgets.QHBoxLayout()
                h_layout.setObjectName('h_layout')
                h_layout.setAlignment(Qt.AlignLeft)
                # Define all variable used
                state = True
                nbr = levels.index(level)
                level_name = level[1]
                msg_label = level_name
                level_path = str(project_path) + level[2]
                icon = QPixmap("Resources/Icons/s-empty.png")
                print('Level Name :', level_name)

                # Test with the Source Control -work only with Perforce
                # TODO Add a progress bar, check levels on sc can be long
                # TODO Setup another Source Control solution -git, subversion
                if sc_software != str('Disabled'):
                    sc = perforce.connect()
                    for file in listdir(dirname(level_path)):
                        # TODO add an operator to sync the files ?
                        # perforce.sync(file, sc)
                        item = join(dirname(level_path), file)
                        item_norm = PureWindowsPath(item)
                        print('Path SC Norm > ', item_norm)
                        revision = perforce.Revision(connection=sc,
                                                     data=str(item_norm))
                        if len(revision.openedBy):
                            print('Level', file, 'opening by someone.')
                            state = False
                            msg_label = 'Level checkout'
                            break

                        if not revision.isSynced:
                            state = False
                            path = "Resources/Icons/cloud-download.png"
                            icon = QPixmap(path)
                            msg_label = 'Not Sync, update it.'
                            print('Level', file, 'not sync.')
                            break

                # Generate the Ui with all parameter
                self.checkBoxLevels[nbr] = QtWidgets.QCheckBox(level_name)
                self.checkBoxLevels[nbr].setObjectName(level_name)
                self.checkBoxLevels[nbr].setEnabled(state)
                self.checkBoxLevels[nbr].setToolTip(msg_label)
                h_layout.addWidget(self.checkBoxLevels[nbr],
                                   alignment=Qt.AlignLeft)
                label_work = QtWidgets.QLabel(group_parent)
                label_work.setPixmap(icon)
                label_work.setToolTip(msg_label)
                h_layout.addWidget(label_work, alignment=Qt.AlignLeft)
                h_layout.addWidget(self.checkBoxLevels[nbr],
                                   alignment=Qt.AlignLeft)
                vertical_parent.addLayout(h_layout)
                # self.allLevelsCheck.contentsMargins()

            if 'False' not in self.scv_data[0]:
                self.checkBoxSubmit.setEnabled(True)

    # Ui Function -------------------------------------------------------------
    #   Bottom Toolbars, option to launch the rendering and the log -----------
    def bottom_tools(self):
        """
        Function to add signal on the bottom toolbars.
        :return:
        """
        self.pushToolsBuils.clicked.connect(self.view_rendering)
        self.pushToolsBuils.setToolTip(self.pushToolsBuils.statusTip())

    # Window Call -------------------------------------------------------------
    #   Tab Setup dialogue ---------------------------------------------------
    def dial_setup_project(self, index):
        """
        Function to show the dialogue 'Setup Tab', when it's close the
        function 'levels_generate' is rebuilt.
        :param index: A simple index to select the Tab opened.
        :return:
        """
        ui_setup_tab = DialSetupTab()
        ui_setup_tab.tabWidget.setCurrentIndex(index)

        ui_setup_tab.show()
        rsp = ui_setup_tab.exec_()

        if rsp == QtWidgets.QDialog.Rejected:
            layout_levels = self.allLevelsCheck
            for item in reversed(range(layout_levels.count())):
                delete_item = layout_levels.itemAt(item).widget()
                layout_levels.removeWidget(delete_item)
                delete_item.setParent(None)
            self.levels_generate()

    # Old, refactoring function -----------------------------------------------
    def dialogue_log(self):
        """
        A simple function to show the Windows Log with all option
        :return:
        """
        dialog_log = DialLogTools(self)
        dialog_log.show()

    def dialogue_help(self, index):
        """
        Function to show the dialogue 'Help'.

        :param index: A simple index to select the Tab opened.
        :return:
        """
        dialog_help = DialViewAbout(self)
        dialog_help.show()
        dialog_help.tabWidget.setCurrentIndex(index)

    def select_level(self, state=0):
        """
        Event to select or deselect all levels,
        :param state: return the state checkbox, 0 to off, 1 to be
        semi-push and 2 to be check
        :return:
        """
        for key, value in self.checkBoxLevels.items():
            btn = self.checkBoxLevels[key]
            if QtWidgets.QAbstractButton.isEnabled(btn):
                btn.setCheckState(state)

    def view_rendering(self):
        """
        Event to launch the rendering windows and all build -check the
        swarm, the source control used... and more.
        :return:
        """
        lvl_rendering = []
        submit_state = False

        for key, value in self.checkBoxLevels.items():
            btn = self.checkBoxLevels[key]
            name = btn.text()
            if QAbstractButton.isChecked(btn):
                lvl_rendering.append(name)

        if len(lvl_rendering) == 0:
            message = 'No level selected !'
            popup_msg(self, 'information', 'Error', message)

        else:
            message = 'Launch the rendering ?'
            reply = popup_msg(self, 'question', 'Rendering', message)
            lvl_rendering.sort()

            if reply == QMessageBox.Yes:
                machines = self.checkBoxMachines
                swarm_setup(QAbstractButton.isChecked(machines))
                if QAbstractButton.isChecked(self.checkBoxSubmit):
                    submit_state = True

                dial_rendering = DialRendering(self,
                                               lvl_list=lvl_rendering,
                                               csv=self.scv_data,
                                               submit=submit_state)
                dial_rendering.show()
                rsp = dial_rendering.exec_()

                if rsp == QtWidgets.QDialog.Accepted:
                    print('Rendering Validate')
                    swarm_setup(False)
                    message = 'Level Build'

            else:
                message = 'Rendering abort.'

        self.statusbar.showMessage(message)