From 8b9fd9e6482668b1b419d319379f8c1e947bf381 Mon Sep 17 00:00:00 2001 From: Silvan Calarco Date: Sat, 19 Oct 2019 16:32:47 +0200 Subject: [PATCH] Initial PyQt5 rewrite of mambabase --- mambabase/FinishPage.ui | 118 ++++++++++ mambabase/InstallationPage.ui | 125 +++++++++++ mambabase/SelectExtraPage.ui | 274 ++++++++++++++++++++++++ mambabase/SelectGroupsPage.ui | 222 +++++++++++++++++++ mambabase/WelcomePage.ui | 167 +++++++++++++++ mambabase/mambabase-pkggroups-parser.sh | 28 +++ mambabase/mambabase.py | 255 ++++++++++++++++++++++ mambabase/openmamba.png | Bin 0 -> 6492 bytes 8 files changed, 1189 insertions(+) create mode 100644 mambabase/FinishPage.ui create mode 100644 mambabase/InstallationPage.ui create mode 100644 mambabase/SelectExtraPage.ui create mode 100644 mambabase/SelectGroupsPage.ui create mode 100644 mambabase/WelcomePage.ui create mode 100755 mambabase/mambabase-pkggroups-parser.sh create mode 100755 mambabase/mambabase.py create mode 100644 mambabase/openmamba.png diff --git a/mambabase/FinishPage.ui b/mambabase/FinishPage.ui new file mode 100644 index 0000000..aefc13f --- /dev/null +++ b/mambabase/FinishPage.ui @@ -0,0 +1,118 @@ + + + Form + + + + 0 + 0 + 552 + 392 + + + + + 0 + 0 + + + + Form + + + + + 0 + 0 + 551 + 391 + + + + + + + + 0 + 0 + + + + background-color: rgb(239, 240, 241); +background-color: rgb(255, 255, 255); + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 511 + 31 + + + + <html><head/><body><p align="center"><span style=" font-size:14pt; font-weight:600;">Installation completed.</span></p></body></html> + + + true + + + + + + 26 + 130 + 501 + 21 + + + + + + + + + + 20 + 100 + 511 + 271 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + 20 + 40 + 511 + 51 + + + + <html><head/><body><p>If you ran into problems, please check the installation log below for warnings or errors and send the report to the openmamba community at <a href="https://openmamba.org"><span style=" text-decoration: underline; color:#2980b9;">openmamba.org</span></a>.</p></body></html> + + + true + + + + + + + + + + diff --git a/mambabase/InstallationPage.ui b/mambabase/InstallationPage.ui new file mode 100644 index 0000000..50b5c64 --- /dev/null +++ b/mambabase/InstallationPage.ui @@ -0,0 +1,125 @@ + + + Form + + + + 0 + 0 + 552 + 392 + + + + + 0 + 0 + + + + Form + + + + + 0 + 0 + 551 + 391 + + + + + + + + 0 + 0 + + + + background-color: rgb(239, 240, 241); +background-color: rgb(255, 255, 255); + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 531 + 31 + + + + <html><head/><body><p align="center"><span style=" font-size:14pt; font-weight:600;">Installation in progress...</span></p></body></html> + + + true + + + + + + 20 + 70 + 511 + 41 + + + + 0 + + + + + + 20 + 120 + 511 + 21 + + + + + + + + + + 20 + 170 + 511 + 201 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 491 + 181 + + + + + + + + + + + + diff --git a/mambabase/SelectExtraPage.ui b/mambabase/SelectExtraPage.ui new file mode 100644 index 0000000..39a2e29 --- /dev/null +++ b/mambabase/SelectExtraPage.ui @@ -0,0 +1,274 @@ + + + Form + + + + 0 + 0 + 552 + 392 + + + + + 0 + 0 + + + + Form + + + + + 0 + 0 + 551 + 391 + + + + + + + + 0 + 0 + + + + background-color: rgb(239, 240, 241); +background-color: rgb(255, 255, 255); + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 511 + 31 + + + + <html><head/><body><p><span style=" font-weight:600;">Please, select from the boxes below the components that you want to install or update from network.</span></p></body></html> + + + true + + + + + + 10 + 50 + 531 + 331 + + + + background-color: rgb(239, 240, 241); + + + Proprietary software installatios/updates + + + + + 20 + 30 + 491 + 21 + + + + NVIDIA proprietary video driver /autodetected) from openmamba.org + + + false + + + + + + 20 + 50 + 491 + 21 + + + + AMD/ATI proprietary video driver from openmamba.org + + + + + + 20 + 70 + 491 + 21 + + + + AMD/ATI proprietary video driver (legacy) from openmamba.org + + + + + + 20 + 90 + 491 + 21 + + + + Broadcom-sta proprietary wireless driver from openmamba.org + + + + + + 20 + 110 + 491 + 21 + + + + Broadcom B43 proprietary firmarwe from openwrt.org + + + + + + 20 + 130 + 491 + 21 + + + + Flash plugin from adobe.com + + + + + + 20 + 170 + 491 + 21 + + + + MS TrueType core fonts from sourceforge + + + + + + 20 + 190 + 491 + 21 + + + + Win32 codecs from mplayerhq.hu + + + + + + 20 + 210 + 491 + 21 + + + + Oracle Java from oracle.com + + + + + + 20 + 150 + 491 + 21 + + + + Pepper-Flash plugin for chromium from google.com + + + + + + 20 + 230 + 491 + 21 + + + + Skype from skype.com + + + + + + 20 + 250 + 491 + 21 + + + + Spotify from spotify.com + + + + + + 20 + 270 + 491 + 21 + + + + VirtualBox extension pack from virtualbox.org + + + + + + 20 + 290 + 491 + 21 + + + + Widevine extension from google.com + + + + + + + + + + + diff --git a/mambabase/SelectGroupsPage.ui b/mambabase/SelectGroupsPage.ui new file mode 100644 index 0000000..a5192ed --- /dev/null +++ b/mambabase/SelectGroupsPage.ui @@ -0,0 +1,222 @@ + + + Form + + + + 0 + 0 + 552 + 392 + + + + + 0 + 0 + + + + Form + + + + + 0 + 0 + 551 + 391 + + + + + + + + 0 + 0 + + + + background-color: rgb(239, 240, 241); +background-color: rgb(255, 255, 255); + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 511 + 31 + + + + <html><head/><body><p><span style=" font-weight:600;">Please, select from the boxes below the components that you want to install or update from network.</span></p></body></html> + + + true + + + + + + 10 + 50 + 531 + 331 + + + + background-color: rgb(239, 240, 241); + + + openmamba installations/updates + + + + + 20 + 30 + 491 + 21 + + + + System base packages (recommended) + + + true + + + + + + 20 + 60 + 491 + 21 + + + + Office packages (OpenOffice, ...) + + + + + + 20 + 90 + 491 + 21 + + + + Audio and Video players (VLC, kaffeine, ...) + + + + + + 20 + 120 + 491 + 21 + + + + Multimedia editing and production (kino, audacity, rosegarden, mixxx, ...) + + + + + + 20 + 150 + 491 + 21 + + + + Internet communication and p2p + + + + + + 20 + 180 + 491 + 21 + + + + Graphics software (gimp, digikam, blender, krita) + + + + + + 20 + 240 + 491 + 21 + + + + Emulation and virtualization (VirtualBox, wine) + + + + + + 20 + 270 + 491 + 21 + + + + Base server packages (Apache, PHP, MariaDB, ...) + + + + + + 20 + 300 + 491 + 21 + + + + Base development packages (gcc, glibc-devel, kernel headers, ...) + + + + + + 20 + 210 + 491 + 21 + + + + Games (tuxracer, pingus, ksudoku, ...) + + + + + + + + + + + diff --git a/mambabase/WelcomePage.ui b/mambabase/WelcomePage.ui new file mode 100644 index 0000000..1118a67 --- /dev/null +++ b/mambabase/WelcomePage.ui @@ -0,0 +1,167 @@ + + + Form + + + + 0 + 0 + 552 + 380 + + + + + 0 + 0 + + + + Form + + + + + 0 + 0 + 551 + 381 + + + + + + + + 0 + 0 + + + + + 0 + 160 + + + + background-color: rgb(255, 255, 255); + + + QFrame::Box + + + QFrame::Raised + + + + + 20 + 10 + 491 + 61 + + + + + 0 + 0 + + + + + + + openmamba.png + + + Qt::AlignCenter + + + + + + 10 + 80 + 511 + 72 + + + + + + + <html><head/><body><p><span style=" font-style:italic;">Copyright (c) 2007-2019 by Silvan Calarco<br/>Released under the terms of the GNU GPL v3 license</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + <html><head/><body><p><a href="https://openmamba.org"><span style=" text-decoration: underline; color:#2980b9;">openmamba.org</span></a></p></body></html> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 0 + 0 + + + + background-color: rgb(239, 240, 241); + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 511 + 81 + + + + <html><head/><body><p>This program will complete the installation by adding the minimum recommended set of packages to the system. Additionally it will let you install, upon selection, macro-group of packages and proprietary software components.</p><p><span style=" font-weight:600;">TIP</span>: you may run this program later from the <span style=" font-style:italic;">openmamba control center</span> on the tray icon bar.</p></body></html> + + + true + + + + + + 10 + 180 + 321 + 20 + + + + <html><head/><body><p><span style=" font-weight:600;">Please, press the Next button to continue.</span></p></body></html> + + + + + + + + + + diff --git a/mambabase/mambabase-pkggroups-parser.sh b/mambabase/mambabase-pkggroups-parser.sh new file mode 100755 index 0000000..42c0ad6 --- /dev/null +++ b/mambabase/mambabase-pkggroups-parser.sh @@ -0,0 +1,28 @@ +#!/bin/bash +. /usr/share/openmamba/pkggroups.db + +echo kernel_mamba=$KERNEL_MAMBA_PKGS +echo kernel_mamba_64gb=$KERNEL_MAMBA_64GB_PKGS +echo kernel_mamba_x86_64=$KERNEL_MAMBA_X86_64_PKGS +echo kernel_mamba_arm=$KERNEL_MAMBA_ARM_PKGS +echo base=$BASE_PKGS +echo base_extras=$BASE_PKGS_EXTRAS +echo office=$OFFICE_PKGS +echo office_extras=$OFFICE_EXTRA_PKGS +echo multimedia_players=$MULTIMEDIA_PLAYERS_PKGS +echo multimedia_players_extras=$MULTIMEDIA_PLAYERS_EXTRA_PKGS +echo multimedia_editing=$MULTIMEDIA_EDITING_PKGS +echo multimedia_editing_extras=$MULTIMEDIA_EDITING_EXTRA_PKGS +echo audio=$AUDIO_PKGS +echo audio_prof=$AUDIO_PROF_PKGS +echo video=$VIDEO_PKGS +echo internet=$INTERNET_PKGS +echo internet_extra=$INTERNET_EXTRA_PKGS +echo graphics=$GRAPHICS_PKGS +echo graphics_extras=$GRAPHICS_EXTRA_PKGS +echo scientific=$SCIENTIFIC_PKGS +echo games=$GAMES_PKGS +echo games_extra=$GAMES_EXTRA_PKGS +echo virtualization=$VIRTUALIZATION_PKGS +echo server=$SERVER_PKGS +echo devel=$DEVEL_PKGS diff --git a/mambabase/mambabase.py b/mambabase/mambabase.py new file mode 100755 index 0000000..3183893 --- /dev/null +++ b/mambabase/mambabase.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 by Silvan Calarco +# Release under the terms of the GPL version 3 license + +from PyQt5 import QtCore +from PyQt5 import QtGui +from PyQt5.QtCore import pyqtProperty +from PyQt5 import QtCore, QtWidgets +from PyQt5 import QtWidgets, uic +from pathlib import Path +import gettext +import subprocess +import os + +import gi +gi.require_version('PackageKitGlib', '1.0') +from gi.repository import PackageKitGlib as packagekit + + +class InstallThread(QtCore.QThread): + parent = None + doneSignal = QtCore.pyqtSignal() + updateProgressSignal = QtCore.pyqtSignal(dict) + pkggroups = {} + + def __init__(self, parent=None): + super(InstallThread, self).__init__(parent) + self.parent = parent + self.doneSignal.connect(parent.installationPage.completeChanged) + self.updateProgressSignal.connect( + parent.installationPage.updateProgressSlot) + # Load packages group db into a dict + result = subprocess.run(['/usr/libexec/mambabase-pkggroups-parser.sh'], + stdout=subprocess.PIPE) + lines = result.stdout.decode('UTF-8').splitlines() + for line in lines: + if line: + (key, val) = line.split("=") + self.pkggroups[key] = val + + def run(self): + install = {} + parent = self.parent + + # Disable back and next buttons + parent.installationPage.done = False + self.doneSignal.emit() + self.updateProgressSignal.emit( + { 'value': 1, 'label': _("Starting installation...")}) + + # Groups + install['base'] = \ + parent.selectGroupsPage.inst_base.isChecked() + install['office'] = \ + parent.selectGroupsPage.inst_office.isChecked() + install['players'] = \ + parent.selectGroupsPage.inst_players.isChecked() + install['multimedia_editing'] = \ + parent.selectGroupsPage.inst_multimedia_editing.isChecked() + install['internet'] = \ + parent.selectGroupsPage.inst_internet.isChecked() + install['graphics'] = \ + parent.selectGroupsPage.inst_graphics.isChecked() + install['games'] = \ + parent.selectGroupsPage.inst_games.isChecked() + install['virtualization'] = \ + parent.selectGroupsPage.inst_virtualization.isChecked() + install['server'] = \ + parent.selectGroupsPage.inst_server.isChecked() + install['devel'] = \ + parent.selectGroupsPage.inst_devel.isChecked() + # Extra proprietary + install['nvidia'] = \ + parent.selectExtraPage.inst_nvidia.isChecked() + install['fglrx'] = \ + parent.selectExtraPage.inst_fglrx.isChecked() + install['fglrx_legacy'] = \ + parent.selectExtraPage.inst_fglrx_legacy.isChecked() + install['broadcom_sta'] = \ + parent.selectExtraPage.inst_broadcom_sta.isChecked() + install['b43'] = \ + parent.selectExtraPage.inst_b43.isChecked() + install['flash'] = \ + parent.selectExtraPage.inst_flash.isChecked() + install['pepperflash'] = \ + parent.selectExtraPage.inst_pepperflash.isChecked() + install['msttcf'] = \ + parent.selectExtraPage.inst_msttcf.isChecked() + install['codecs'] = \ + parent.selectExtraPage.inst_codecs.isChecked() + install['java'] = \ + parent.selectExtraPage.inst_java.isChecked() + install['skype'] = \ + parent.selectExtraPage.inst_skype.isChecked() + install['spotify'] = \ + parent.selectExtraPage.inst_spotify.isChecked() + install['virtualbox'] = \ + parent.selectExtraPage.inst_virtualbox.isChecked() + install['widevine'] = \ + parent.selectExtraPage.inst_widevine.isChecked() + + # Update packages list + self.updateProgressSignal.emit({ 'value': 5, + 'label': _("Updating packages list...")}) + + result = subprocess.run(['pkcon', 'refresh', 'force'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode == 0: + if result.stdout: + self.updateProgressSignal.emit({ + 'details': result.stdout.decode('UTF-8')}) + else: + if result.stderr: + self.updateProgressSignal.emit({ + 'details': result.stderr.decode('UTF-8')}) + + # Perform system update + self.updateProgressSignal.emit({ 'value': 10, + 'label': _("Updating installed packages...")}) + + result = subprocess.run(['pkcon', 'update'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode == 0: + if result.stdout: + self.updateProgressSignal.emit({ + 'details': result.stdout.decode('UTF-8')}) + else: + if result.stderr: + self.updateProgressSignal.emit({ + 'details': result.stderr.decode('UTF-8')}) + + self.updateProgressSignal.emit({ 'value': 20 }) + + # Install requested packages + arch = os.uname()[4] + if arch == 'i686': + arch = 'i586' + elif arch[:3] == 'arm': + arch = 'arm' + client = packagekit.Client() + for inst in install: + if install[inst]: + parent.installationPage.progressLabel.setText( + _("Installing %s packages..." % inst)) + for p in self.pkggroups[inst].split(): + result =client.resolve(0, (p,), None, + self.packagekit_progress_cb, None) + pkgs = result.get_package_array() + for p in pkgs: + if 'installed' in p.get_data().split(':'): + break + if p.get_arch() != arch: + continue + packageid = p.get_name() + ';' + p.get_version() + ';' \ + + p.get_arch() + ';' + p.get_data() + client.install_packages(False, (packageid, ), None, + self.packagekit_progress_cb, p.get_name()) + + self.updateProgressSignal.emit({ 'value': 100, + 'label': _("Installation finished!")}) + + # Enable back and next buttons + parent.installationPage.done = True + self.doneSignal.emit() + + def packagekit_progress_cb(self, status, typ, data=None): + if status.get_property('package'): + self.updateProgressSignal.emit({ + 'label': _("Installing %s package (%s)..." % + (status.get_property('package').get_name(), + str(status.get_percentage())))}) + + +class MambabaseWizard(QtWidgets.QWizard): + def __init__(self, parent=None): + super(MambabaseWizard, self).__init__(parent) + self.addPage(WelcomePage(self)) + self.selectGroupsPage = SelectGroupsPage(self) + self.addPage(self.selectGroupsPage) + self.selectExtraPage = SelectExtraPage(self) + self.addPage(self.selectExtraPage) + self.installationPage = InstallationPage(self) + self.addPage(self.installationPage) + self.finishPage = FinishPage(self) + self.addPage(self.finishPage) + self.setWindowTitle("openmamba base network installations - openmamba.org") + self.setFixedSize(571,465) + self.currentIdChanged.connect(self.currentIdChangedSlot) + self.installThread = InstallThread(self) + + def currentIdChangedSlot(self, currentId): + if currentId == 3: + self.installThread.start() + + +class WelcomePage(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super(WelcomePage, self).__init__(parent) + uic.loadUi('WelcomePage.ui', self) + self.show() + + +class SelectGroupsPage(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super(SelectGroupsPage, self).__init__(parent) + uic.loadUi('SelectGroupsPage.ui', self) + self.show() + + +class SelectExtraPage(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super(SelectExtraPage, self).__init__(parent) + uic.loadUi('SelectExtraPage.ui', self) + self.setCommitPage(True) + self.show() + + +class InstallationPage(QtWidgets.QWizardPage): + done = False + parent = None + + def __init__(self, parent=None): + super(InstallationPage, self).__init__(parent) + uic.loadUi('InstallationPage.ui', self) + self.parent = parent + self.show() + + def isComplete(self): + return self.done + + @QtCore.pyqtSlot(dict) + def updateProgressSlot(self, dict): + if 'value' in dict: + self.progressBar.setValue(dict['value']) + if 'label' in dict: + self.progressLabel.setText(dict['label']) + if 'details' in dict: + self.progressDetails.setText(dict['details']) + + +class FinishPage(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super(FinishPage, self).__init__(parent) + uic.loadUi('FinishPage.ui', self) + self.show() + + +if __name__ == '__main__': + import sys + app = QtWidgets.QApplication(sys.argv) + app.setWindowIcon(QtGui.QIcon("mamba-128x128.png")) + wizard = MambabaseWizard() + gettext.install('mambabase', '/usr/share/locale') + wizard.show() + sys.exit(app.exec_()) diff --git a/mambabase/openmamba.png b/mambabase/openmamba.png new file mode 100644 index 0000000000000000000000000000000000000000..ff941ba1b3f0b76849ae5751769cf1b4b1b1862a GIT binary patch literal 6492 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