Esempio n. 1
0
    def save_requirements(self):
        """
        Ask if they want to save the requirements of the
        created virtual environment.
        """
        self.setEnabled(False)

        msg_box_question = QMessageBox.question(
            self, "Save requirements",
            "Do you want to generate a requirements?",
            QMessageBox.Yes | QMessageBox.No)

        if msg_box_question == QMessageBox.Yes:
            venv_dir = os.path.join(self.venv_location, self.venv_name)
            save_file = QFileDialog.getSaveFileName(
                self,
                "Save requirements",
                directory=f"{venv_dir}/requirements.txt")
            save_path = save_file[0]

            if save_path != "":
                self.manager = PipManager(self.venv_location, self.venv_name)
                self.manager.run_pip(creator.cmds[2], [">", save_path])

                msg_txt = (f"Saved requirements in: \n{save_path}")
                QMessageBox.information(self, "Saved", msg_txt)
                logger.info(f"Saved '{save_path}'...")
                self.wizard().next()
        else:
            self.wizard().next()

        self.setEnabled(True)
Esempio n. 2
0
    def install_requirements(self):
        """Install the packages from the specified requirements file.
        """
        self.setEnabled(False)

        self.console.setWindowTitle("Creating environment")
        logger.info("Creating environment...")

        # open the console when recieving signal from manager
        self.manager = PipManager(self.venv_location, f"'{self.venv_name}'")
        self.manager.started.connect(self.console.exec_)

        # start installing packages from requirements file
        #print(f"[PROCESS]: Installing packages from '{self.requirements}'")
        self.manager.run_pip(creator.cmds[0],
                             [creator.opts[1], f"'{self.requirements}'"])

        # display the updated output
        self.manager.textChanged.connect(self.console.update_status)

        # show info dialog
        self.manager.failed.connect(self.console.finish_fail)
        self.manager.failed.connect(self.console.close)

        # clear the contents when closing console
        if self.console.close:
            self.console.console_window.clear()

        self.setEnabled(True)
Esempio n. 3
0
    def install_venv(self, args):
        """
        Execute the commands to create the environment.
        """
        self.started.emit()
        logger.info("Creating virtual environment...")

        py_vers, name, location, with_pip, site_packages = args
        env_dir = os.path.join(location, f"'{name}'")

        create_venv(py_vers,
                    env_dir,
                    with_pip=with_pip,
                    system_site_packages=site_packages
                    #symlinks=symlinks
                    )

        if with_pip:
            # update pip to the latest version
            self.manager = PipManager(location, f"'{name}'")
            self.updatePipMsg.emit()
            self.manager.run_pip(cmds[0], [opts[0], "pip"])
            self.manager.finished.connect(self.finished.emit)

        else:
            self.finished.emit()
Esempio n. 4
0
    def deptree_packages(self, event, style):
        """
        Test if `pipdeptree` is installed and ask user wether to
        install it if it's not. Then call `self.list_packages()`
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()
        pipdeptree_binary = os.path.join(active_dir, venv, "bin", "pipdeptree")
        has_pipdeptree = os.path.exists(pipdeptree_binary)
        message_txt = (
            "This requires the pipdeptree package\nto be installed.\n\n"
            "Do you want to install it?\n")

        if has_pipdeptree:
            self.list_packages(event, style)
        else:
            if self.has_pip(active_dir, venv):
                msg_box_confirm = QMessageBox.question(
                    self, "Confirm", message_txt,
                    QMessageBox.Yes | QMessageBox.Cancel)
                if msg_box_confirm == QMessageBox.Yes:
                    self.progress_bar.setWindowTitle("Installing")
                    self.progress_bar.status_label.setText(
                        "Installing pipdeptree...")
                    logger.info("Installing pipdeptree...")

                    self.manager = PipManager(active_dir, venv)
                    self.manager.run_pip(creator.cmds[0],
                                         [creator.opts[0], "pipdeptree"])
                    self.manager.started.connect(self.progress_bar.exec_)
                    self.manager.finished.connect(self.progress_bar.close)
                    self.manager.process_stop()
                    self.list_packages(event, style)
Esempio n. 5
0
    def save_requires(self, event):
        """
        Write the requirements of the selected environment to file.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()
        venv_dir = os.path.join(active_dir, venv)

        if self.has_pip(active_dir, venv):
            save_file = QFileDialog.getSaveFileName(
                self,
                "Save requirements",
                directory=f"{venv_dir}/requirements.txt")
            save_path = save_file[0]

            if save_path != "":
                logger.info(f"Saving '{save_path}'...")

                # write 'pip freeze' output to selected file
                self.manager = PipManager(active_dir, venv)
                self.manager.run_pip(creator.cmds[2], [">", save_path])

                # show an info message
                message_txt = (f"Saved requirements in \n{save_path}")
                QMessageBox.information(self, "Saved", message_txt)
Esempio n. 6
0
    def install_local(self, event):
        """Install from a local project.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()

        if self.has_pip(active_dir, venv):
            project_dir = QFileDialog.getExistingDirectory(
                self, "Select project directory")
            project_name = os.path.basename(project_dir)

            if project_dir != "":
                self.console.setWindowTitle(f"Installing {project_name}")
                logger.info("Installing from local project path...")

                self.manager = PipManager(active_dir, venv)
                self.manager.run_pip(creator.cmds[0],
                                     [creator.opts[2], f"'{project_dir}'"])
                self.manager.started.connect(self.console.exec_)

                # display the updated output
                self.manager.textChanged.connect(self.console.update_status)

                # clear the content on window close
                if self.console.close:
                    self.console.console_window.clear()
Esempio n. 7
0
    def install_requires(self, event):
        """
        Install packages from a requirements file into the
        selected environment.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()

        if self.has_pip(active_dir, venv):
            file_name = QFileDialog.getOpenFileName(self,
                                                    "Select a requirements")
            file_path = file_name[0]

            if file_path != "":
                creator.fix_requirements(file_path)
                self.console.setWindowTitle("Installing from requirements")
                logger.info("Installing from requirements...")

                self.manager = PipManager(active_dir, venv)
                self.manager.run_pip(creator.cmds[0],
                                     [creator.opts[1], f"'{file_path}'"])
                self.manager.started.connect(self.console.exec_)

                # display the updated output
                self.manager.textChanged.connect(self.console.update_status)

                # clear the content on window close
                if self.console.close:
                    self.console.console_window.clear()
Esempio n. 8
0
    def upgrade_pip(self, event):
        """Run `pip install --upgrade pip` command.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()

        if self.has_pip(active_dir, venv):
            self.console.setWindowTitle("Updating Pip")
            logger.info("Attempting to update Pip...")

            self.manager = PipManager(active_dir, venv)
            self.manager.run_pip(creator.cmds[0], [creator.opts[0], "pip"])
            self.manager.started.connect(self.console.exec_)

            # display the updated output
            self.manager.textChanged.connect(self.console.update_status)

            # clear the content on window close
            if self.console.close:
                self.console.console_window.clear()
Esempio n. 9
0
    def list_packages(self, event, style):
        """
        Open console dialog and list the installed packages. The argument
        `style` controls which style the output should have: `style=1` for
        `pip list`, `style=2` for `pip freeze` and style=3 for a dependency
        output via `pipdeptree`.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()

        if self.has_pip(active_dir, venv):
            self.console.setWindowTitle(f"Packages installed in:  {venv}")

            self.manager = PipManager(active_dir, f"'{venv}'")
            self.manager.run_pip(creator.cmds[style])
            self.manager.started.connect(self.console.exec_)

            # display the updated output
            self.manager.textChanged.connect(self.console.update_status)

            # clear the content on window close
            if self.console.close:
                self.console.console_window.clear()
Esempio n. 10
0
    def install_package(self):
        """
        Get the name of the selected item from the results table. Ask user
        for confirmation before installing. If user confirmes, install the
        selected package into the created virtual environment, else abort.
        """
        indexes = self.results_table.selectionModel().selectedRows()
        for index in sorted(indexes):
            self.pkg = index.data()

        msg_box_question = QMessageBox.question(
            self, "Confirm", f"Are you sure you want to install '{self.pkg}'?",
            QMessageBox.Yes | QMessageBox.Cancel)

        if msg_box_question == QMessageBox.Yes:
            self.console.setWindowTitle(f"Installing {self.pkg}")

            self.manager = PipManager(self.venv_location,
                                      f"'{self.venv_name}'")
            # open the console when recieving signal from manager
            self.manager.started.connect(self.console.exec_)

            # start installing the selected package
            logger.info(f"Installing '{self.pkg}'...")
            self.manager.run_pip(creator.cmds[0], [creator.opts[0], self.pkg])

            # display the updated output
            self.manager.textChanged.connect(self.console.update_status)

            # clear the content when closing console
            if self.console.close:
                self.console.console_window.clear()

                # clear search input line
                self.pkg_name_line.clear()
                self.pkg_name_line.setFocus(True)
Esempio n. 11
0
class InstallPackages(QWizardPage):
    """
    Install packages via Pip into the created virtual environment.
    """
    def __init__(self):
        super().__init__()

        self.setTitle("Install Packages")
        self.setSubTitle(
            "Specify the packages you want to install into the virtual "
            "environment. Right-click on the item you want to install. "
            "You can install as many packages as you need. When finished "
            "click next.")

        self.console = ConsoleDialog()

        #]===================================================================[#
        #] PAGE CONTENT [#===================================================[#
        #]===================================================================[#

        grid_layout = QGridLayout(self)

        pkg_name_label = QLabel("Package &name:")
        self.pkg_name_line = QLineEdit()
        pkg_name_label.setBuddy(self.pkg_name_line)

        self.search_button = QPushButton("&Search",
                                         clicked=self.pop_results_table)

        # results table
        self.results_table = ResultsTable(
            selectionBehavior=QAbstractItemView.SelectRows,
            editTriggers=QAbstractItemView.NoEditTriggers,
            alternatingRowColors=True,
            sortingEnabled=True,
            doubleClicked=self.install_package,
            context_triggered=self.install_package  # signal
        )

        # adjust vertical headers
        v_header = self.results_table.verticalHeader()
        v_header.setDefaultSectionSize(28)
        v_header.hide()

        # adjust horizontal headers
        h_header = self.results_table.horizontalHeader()
        h_header.setDefaultAlignment(Qt.AlignLeft)
        h_header.setDefaultSectionSize(120)
        h_header.setStretchLastSection(True)
        h_header.setSectionResizeMode(QHeaderView.ResizeToContents)

        # item model
        self.results_model = QStandardItemModel(0, 2, self)
        self.results_table.setModel(self.results_model)

        grid_layout.addWidget(pkg_name_label, 0, 0, 1, 1)
        grid_layout.addWidget(self.pkg_name_line, 0, 1, 1, 1)
        grid_layout.addWidget(self.search_button, 0, 2, 1, 1)
        grid_layout.addWidget(self.results_table, 1, 0, 1, 3)

    def initializePage(self):
        self.python_version = self.field("python_version")
        self.python_path = self.field("python_path")
        self.venv_name = self.field("venv_name")
        self.venv_location = self.field("venv_location")
        self.requirements = self.field("requirements")

        # clear all inputs and contents
        self.results_model.clear()
        self.pkg_name_line.clear()
        self.pkg_name_line.setFocus(True)

        # set text in column headers
        self.results_model.setHorizontalHeaderLabels(
            ["Name", "Version", "Description"])

        # remove focus from 'next' button
        QTimer.singleShot(0, lambda: self.next_button.setDefault(False))

        # set focus on 'search' button
        QTimer.singleShot(0, lambda: self.search_button.setDefault(True))

        # disable 'back' button
        back_button = self.wizard().button(QWizard.BackButton)
        QTimer.singleShot(0, lambda: back_button.setEnabled(False))

        if self.wizard().basic_settings.with_pip_check_box.isChecked():
            # connect 'next' button to self.save_requirements()
            self.next_button = self.wizard().button(QWizard.NextButton)
            self.next_button.disconnect()
            self.next_button.clicked.connect(self.save_requirements)

        # run the installer if self.requirements holds a str
        if len(self.requirements) > 0:
            try:
                creator.fix_requirements(self.requirements)
            except FileNotFoundError:
                pass  # the gui will show an error message
            self.install_requirements()

    def install_requirements(self):
        """Install the packages from the specified requirements file.
        """
        self.setEnabled(False)

        self.console.setWindowTitle("Creating environment")
        logger.info("Creating environment...")

        # open the console when recieving signal from manager
        self.manager = PipManager(self.venv_location, f"'{self.venv_name}'")
        self.manager.started.connect(self.console.exec_)

        # start installing packages from requirements file
        #print(f"[PROCESS]: Installing packages from '{self.requirements}'")
        self.manager.run_pip(creator.cmds[0],
                             [creator.opts[1], f"'{self.requirements}'"])

        # display the updated output
        self.manager.textChanged.connect(self.console.update_status)

        # show info dialog
        self.manager.failed.connect(self.console.finish_fail)
        self.manager.failed.connect(self.console.close)

        # clear the contents when closing console
        if self.console.close:
            self.console.console_window.clear()

        self.setEnabled(True)

    def pop_results_table(self):
        """Refresh the results table.
        """
        search_item = self.pkg_name_line.text()

        self.results_model.setRowCount(0)

        for info in get_data.get_package_infos(search_item):
            self.results_model.insertRow(0)

            for i, text in enumerate(
                (info.pkg_name, info.pkg_version, info.pkg_summary)):
                self.results_model.setItem(0, i, QStandardItem(text))

        if not get_data.get_package_infos(search_item):
            logger.debug(f"No matches for '{search_item}'")
            QMessageBox.information(self, "No result",
                                    f"No result matching '{search_item}'.\n")

    def install_package(self):
        """
        Get the name of the selected item from the results table. Ask user
        for confirmation before installing. If user confirmes, install the
        selected package into the created virtual environment, else abort.
        """
        indexes = self.results_table.selectionModel().selectedRows()
        for index in sorted(indexes):
            self.pkg = index.data()

        msg_box_question = QMessageBox.question(
            self, "Confirm", f"Are you sure you want to install '{self.pkg}'?",
            QMessageBox.Yes | QMessageBox.Cancel)

        if msg_box_question == QMessageBox.Yes:
            self.console.setWindowTitle(f"Installing {self.pkg}")

            self.manager = PipManager(self.venv_location,
                                      f"'{self.venv_name}'")
            # open the console when recieving signal from manager
            self.manager.started.connect(self.console.exec_)

            # start installing the selected package
            logger.info(f"Installing '{self.pkg}'...")
            self.manager.run_pip(creator.cmds[0], [creator.opts[0], self.pkg])

            # display the updated output
            self.manager.textChanged.connect(self.console.update_status)

            # clear the content when closing console
            if self.console.close:
                self.console.console_window.clear()

                # clear search input line
                self.pkg_name_line.clear()
                self.pkg_name_line.setFocus(True)

    def save_requirements(self):
        """
        Ask if they want to save the requirements of the
        created virtual environment.
        """
        self.setEnabled(False)

        msg_box_question = QMessageBox.question(
            self, "Save requirements",
            "Do you want to generate a requirements?",
            QMessageBox.Yes | QMessageBox.No)

        if msg_box_question == QMessageBox.Yes:
            venv_dir = os.path.join(self.venv_location, self.venv_name)
            save_file = QFileDialog.getSaveFileName(
                self,
                "Save requirements",
                directory=f"{venv_dir}/requirements.txt")
            save_path = save_file[0]

            if save_path != "":
                self.manager = PipManager(self.venv_location, self.venv_name)
                self.manager.run_pip(creator.cmds[2], [">", save_path])

                msg_txt = (f"Saved requirements in: \n{save_path}")
                QMessageBox.information(self, "Saved", msg_txt)
                logger.info(f"Saved '{save_path}'...")
                self.wizard().next()
        else:
            self.wizard().next()

        self.setEnabled(True)
Esempio n. 12
0
class VenvTable(BaseTable):
    """List the virtual environments found.
    """
    started = pyqtSignal()
    finished = pyqtSignal()
    text_changed = pyqtSignal(str)
    refresh = pyqtSignal()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.drive_icon = QIcon(self.style().standardIcon(
            QStyle.SP_DriveHDIcon))
        self.delete_icon = QIcon(self.style().standardIcon(
            QStyle.SP_TrashIcon))
        self.save_icon = QIcon(self.style().standardIcon(
            QStyle.SP_DialogSaveButton))
        self.folder_icon = QIcon(self.style().standardIcon(
            QStyle.SP_DirOpenIcon))

        self.progress_bar = ProgBarDialog()
        self.console = ConsoleDialog()
        self.thread = QThread(self)
        self.m_clone_repo_worker = CloningWorker()

        # thread
        self.thread.start()
        self.m_clone_repo_worker.moveToThread(self.thread)
        self.m_clone_repo_worker.started.connect(self.progress_bar.exec_)
        self.m_clone_repo_worker.finished.connect(self.progress_bar.close)
        self.m_clone_repo_worker.finished.connect(self.finish_info)

        # perform a proper stop using quit() and wait()
        self.thread.finished.connect(self.thread.quit)
        self.thread.finished.connect(self.thread.wait)

    def contextMenuEvent(self, event):
        context_menu = QMenu(self)

        # pop up only if clicking on a row
        if self.indexAt(event.pos()).isValid():
            context_menu.popup(QCursor.pos())

        # sub menus
        details_sub_menu = QMenu("Det&ails", self, icon=self.info_icon)
        install_sub_menu = QMenu("&Install",
                                 self,
                                 icon=QIcon.fromTheme("software-install"))
        editable_sub_menu = QMenu("&Editable",
                                  self,
                                  icon=QIcon.fromTheme("software-install"))

        #]===================================================================[#
        #] ACTIONS [#========================================================[#
        #]===================================================================[#

        upgrade_pip_action = QAction(
            QIcon.fromTheme("system-software-update"),
            "&Upgrade Pip to latest",
            self,
            statusTip="Upgrade Pip to the latest version")
        upgrade_pip_action.triggered.connect(lambda: self.upgrade_pip(event))

        install_packages_action = QAction(
            "Install &additional packages",
            self,
            statusTip="Install additional packages")
        install_packages_action.triggered.connect(
            lambda: self.add_packages(event))

        install_requires_action = QAction(
            "Install from &requirements",
            self,
            statusTip="Install packages from requirements")
        install_requires_action.triggered.connect(
            lambda: self.install_requires(event))

        install_local_action = QAction("Install &local project",
                                       self,
                                       statusTip="Install a local project")
        install_local_action.triggered.connect(
            lambda: self.install_local(event))

        install_vsc_action = QAction("Install from &repository",
                                     self,
                                     statusTip="Install from VSC repository")
        install_vsc_action.triggered.connect(lambda: self.install_vsc(event))

        save_requires_action = QAction(self.save_icon,
                                       "Save &requirements",
                                       self,
                                       statusTip="Write requirements to file")
        save_requires_action.triggered.connect(
            lambda: self.save_requires(event))

        list_packages_action = QAction("&List installed packages",
                                       self,
                                       statusTip="List installed packages")
        list_packages_action.triggered.connect(
            lambda: self.list_packages(event, style=1))

        list_freeze_action = QAction(
            "Show &freeze output",
            self,
            statusTip="List the output of 'pip freeze'")
        list_freeze_action.triggered.connect(
            lambda: self.freeze_packages(event, style=2))

        list_deptree_action = QAction(
            "Display &dependency tree",
            self,
            statusTip="List dependencies with 'pipdeptree'")
        list_deptree_action.triggered.connect(
            lambda: self.deptree_packages(event, style=3))

        open_venv_dir_action = QAction(
            self.folder_icon,
            "&Open containing folder",
            self,
            statusTip="Open the folder containing the virtual environment")
        open_venv_dir_action.triggered.connect(
            lambda: self.open_venv_dir(event))

        delete_venv_action = QAction(self.delete_icon,
                                     "&Delete environment",
                                     self,
                                     statusTip="Delete environment")
        delete_venv_action.triggered.connect(lambda: self.delete_venv(event))

        #]===================================================================[#
        #] MENUS [#==========================================================[#
        #]===================================================================[#

        context_menu.addAction(upgrade_pip_action)

        # install sub menu
        context_menu.addMenu(install_sub_menu)
        install_sub_menu.addAction(install_packages_action)
        install_sub_menu.addAction(install_requires_action)
        install_sub_menu.addAction(install_local_action)
        install_sub_menu.addAction(install_vsc_action)

        # details sub meun
        context_menu.addMenu(details_sub_menu)
        details_sub_menu.addAction(list_packages_action)
        details_sub_menu.addAction(list_freeze_action)
        details_sub_menu.addAction(list_deptree_action)

        context_menu.addAction(save_requires_action)
        context_menu.addAction(open_venv_dir_action)
        context_menu.addAction(delete_venv_action)

    def valid_version(self, venv_path):
        """Test wether the Python version required is installed.
        """
        cfg_file = os.path.join(venv_path, "pyvenv.cfg")
        is_installed = get_data.get_pyvenv_cfg(cfg_file, "installed")
        version = get_data.get_pyvenv_cfg(cfg_file, "version")
        py_path = get_data.get_pyvenv_cfg(cfg_file, "py_path")
        msg_txt = (
            f"This environment requires {version} \nfrom {py_path} which is \nnot installed.\n"
        )

        if is_installed == "no":
            msg_box = QMessageBox(QMessageBox.Critical, "Error", msg_txt,
                                  QMessageBox.Ok, self)
            msg_box.exec_()
            return False
        return True

    def venv_exists(self, path):
        """
        Test wether the directory of the selected environment actually exists.
        """
        if os.path.exists(path):
            return True

        msg_box = QMessageBox(QMessageBox.Critical, "Error",
                              "Selected environment could not be found.",
                              QMessageBox.Ok, self)
        msg_box.exec_()
        self.refresh.emit()
        return False

    def has_pip(self, venv_dir, venv_name):
        """Test if `pip` is installed.
        """
        venv_path = os.path.join(venv_dir, venv_name)
        pip_binary = os.path.join(venv_path, "bin", "pip")
        has_pip = os.path.exists(pip_binary)

        if self.venv_exists(venv_path) and self.valid_version(venv_path):
            if has_pip:
                return True
            QMessageBox.information(self, "Info",
                                    "This environment has no Pip installed.")
            return False
        return False

    def upgrade_pip(self, event):
        """Run `pip install --upgrade pip` command.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()

        if self.has_pip(active_dir, venv):
            self.console.setWindowTitle("Updating Pip")
            logger.info("Attempting to update Pip...")

            self.manager = PipManager(active_dir, venv)
            self.manager.run_pip(creator.cmds[0], [creator.opts[0], "pip"])
            self.manager.started.connect(self.console.exec_)

            # display the updated output
            self.manager.textChanged.connect(self.console.update_status)

            # clear the content on window close
            if self.console.close:
                self.console.console_window.clear()

    def add_packages(self, event):
        """
        Install additional packages into the selected environment.
        """
        pass

    def install_requires(self, event):
        """
        Install packages from a requirements file into the
        selected environment.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()

        if self.has_pip(active_dir, venv):
            file_name = QFileDialog.getOpenFileName(self,
                                                    "Select a requirements")
            file_path = file_name[0]

            if file_path != "":
                creator.fix_requirements(file_path)
                self.console.setWindowTitle("Installing from requirements")
                logger.info("Installing from requirements...")

                self.manager = PipManager(active_dir, venv)
                self.manager.run_pip(creator.cmds[0],
                                     [creator.opts[1], f"'{file_path}'"])
                self.manager.started.connect(self.console.exec_)

                # display the updated output
                self.manager.textChanged.connect(self.console.update_status)

                # clear the content on window close
                if self.console.close:
                    self.console.console_window.clear()

    def install_local(self, event):
        """Install from a local project.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()

        if self.has_pip(active_dir, venv):
            project_dir = QFileDialog.getExistingDirectory(
                self, "Select project directory")
            project_name = os.path.basename(project_dir)

            if project_dir != "":
                self.console.setWindowTitle(f"Installing {project_name}")
                logger.info("Installing from local project path...")

                self.manager = PipManager(active_dir, venv)
                self.manager.run_pip(creator.cmds[0],
                                     [creator.opts[2], f"'{project_dir}'"])
                self.manager.started.connect(self.console.exec_)

                # display the updated output
                self.manager.textChanged.connect(self.console.update_status)

                # clear the content on window close
                if self.console.close:
                    self.console.console_window.clear()

    def install_vsc(self, event):
        """Install from a VSC repository.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()
        venv_bin = os.path.join(active_dir, venv, "bin", "python")

        if self.has_pip(active_dir, venv):
            url, ok = QInputDialog.getText(
                self, "Specify VSC project url",
                "Enter url to repository:" + " " * 65)

            if url != "":
                project_name = os.path.basename(url)
                project_url = f"git+{url}#egg={project_name}"
                cmd = (
                    f"{venv_bin} -m pip install --no-cache-dir -e {project_url}"
                )
                self.progress_bar.setWindowTitle(f"Installing {project_name}")
                self.progress_bar.status_label.setText("Cloning repository...")
                logger.info(f"Installing {project_name}...")

                wrapper = partial(self.m_clone_repo_worker.run_process, cmd)
                QTimer.singleShot(0, wrapper)

    def finish_info(self):
        """
        Show an info message when the cloning process has finished successfully.
        """
        msg_txt = (
            "Successfully installed package       \nfrom VSC repository.\n")
        QMessageBox.information(self, "Done", msg_txt)

    def save_requires(self, event):
        """
        Write the requirements of the selected environment to file.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()
        venv_dir = os.path.join(active_dir, venv)

        if self.has_pip(active_dir, venv):
            save_file = QFileDialog.getSaveFileName(
                self,
                "Save requirements",
                directory=f"{venv_dir}/requirements.txt")
            save_path = save_file[0]

            if save_path != "":
                logger.info(f"Saving '{save_path}'...")

                # write 'pip freeze' output to selected file
                self.manager = PipManager(active_dir, venv)
                self.manager.run_pip(creator.cmds[2], [">", save_path])

                # show an info message
                message_txt = (f"Saved requirements in \n{save_path}")
                QMessageBox.information(self, "Saved", message_txt)

    def list_packages(self, event, style):
        """
        Open console dialog and list the installed packages. The argument
        `style` controls which style the output should have: `style=1` for
        `pip list`, `style=2` for `pip freeze` and style=3 for a dependency
        output via `pipdeptree`.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()

        if self.has_pip(active_dir, venv):
            self.console.setWindowTitle(f"Packages installed in:  {venv}")

            self.manager = PipManager(active_dir, f"'{venv}'")
            self.manager.run_pip(creator.cmds[style])
            self.manager.started.connect(self.console.exec_)

            # display the updated output
            self.manager.textChanged.connect(self.console.update_status)

            # clear the content on window close
            if self.console.close:
                self.console.console_window.clear()

    def freeze_packages(self, event, style):
        """Print `pip freeze` output to console window.
        """
        self.list_packages(event, style)

    def deptree_packages(self, event, style):
        """
        Test if `pipdeptree` is installed and ask user wether to
        install it if it's not. Then call `self.list_packages()`
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()
        pipdeptree_binary = os.path.join(active_dir, venv, "bin", "pipdeptree")
        has_pipdeptree = os.path.exists(pipdeptree_binary)
        message_txt = (
            "This requires the pipdeptree package\nto be installed.\n\n"
            "Do you want to install it?\n")

        if has_pipdeptree:
            self.list_packages(event, style)
        else:
            if self.has_pip(active_dir, venv):
                msg_box_confirm = QMessageBox.question(
                    self, "Confirm", message_txt,
                    QMessageBox.Yes | QMessageBox.Cancel)
                if msg_box_confirm == QMessageBox.Yes:
                    self.progress_bar.setWindowTitle("Installing")
                    self.progress_bar.status_label.setText(
                        "Installing pipdeptree...")
                    logger.info("Installing pipdeptree...")

                    self.manager = PipManager(active_dir, venv)
                    self.manager.run_pip(creator.cmds[0],
                                         [creator.opts[0], "pipdeptree"])
                    self.manager.started.connect(self.progress_bar.exec_)
                    self.manager.finished.connect(self.progress_bar.close)
                    self.manager.process_stop()
                    self.list_packages(event, style)

    def open_venv_dir(self, event):
        """Open the selected venv directory.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()
        venv_dir = os.path.join(active_dir, venv)

        if os.path.isdir(venv_dir):
            os.system(f"xdg-open '{venv_dir}'")

    def delete_venv(self, event):
        """
        Delete the selected virtual environment by clicking
        delete from the context menu in venv table.
        """
        active_dir = get_data.get_active_dir_str()
        venv = self.get_selected_item()
        venv_path = os.path.join(active_dir, venv)

        if self.venv_exists(venv_path):
            msg_box_critical = QMessageBox.critical(
                self, "Confirm", f"Are you sure you want to delete '{venv}'?",
                QMessageBox.Yes | QMessageBox.Cancel)
            if msg_box_critical == QMessageBox.Yes:
                shutil.rmtree(venv_path)
                logging.info(f"Successfully deleted '{venv_path}'")
                self.refresh.emit()