From dc2491e5fb82ffc98ebb1aaa08c4377a0c1edc69 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 4 Sep 2018 06:52:29 +0200 Subject: [PATCH] Show khelpcenter instead QWebView() --- README.md | 220 ---------------------- man/en/snapman.1.rst | 2 +- src/Snapman/about.py | 56 ------ src/Snapman/about.py-qwebkit.noworkasroot | 206 ++++++++++++++++++++ src/Snapman/gui.py | 15 +- src/Snapman/section.py | 37 ++-- 6 files changed, 243 insertions(+), 293 deletions(-) delete mode 100644 README.md create mode 100644 src/Snapman/about.py-qwebkit.noworkasroot diff --git a/README.md b/README.md deleted file mode 100644 index fe3a531..0000000 --- a/README.md +++ /dev/null @@ -1,220 +0,0 @@ -![snapman-preview](https://github.com/mdomlop/snapman/blob/master/preview.png "snapman interface") - -Snapman -------- - -Snapman is a backup program based on the ability of Btrfs file system to -capture snapshots of subvolumes. - -When you run `snapman` it will read its configuration file and then it will -make backups (snapshots) of indicated subvolumes at desired frequency until -reach a defined quota. If quota was reached, then it will remove the older -backup before to make a new one to keep the number of backups indicated in -quota. - -Snapman will run periodically if you use the `--daemon` command line option. - -Backups are stored in a directory specified in the configuration file. - -Since version 1.0a Snapman has an integrated GUI interface. It can be -activated using the `--gui` command line option. - -For each subvolume that you want to backup you must to write at least an entry -in the configuration file (in the easy `.ini` format). - -Snapman accepts command-line options to manage snapshots. - -For futher details, read the included documentation: - -- [Snapman manual](man/en/snapman.1.rst), for the manual. - -- [Configuration manual](man/en/snapman.5.rst), for the manual of configuration - file syntax. - -When the program is installed, all the documentation are available at -`/usr/share/doc/snapman`. And the manuals with: - - man snapman - -And, for configuration file syntax: - - man 5 snapman - - -Usage ------ - -First of all edit the configuration file at `/etc/snapman.ini`. A default one is -supplied as example. Alternatively you can use the `--sample` command line -option to generate one: - - # snapman --sample > /etc/snapman.ini - -Then edit `/etc/snapman.ini` at your preferences. You can edit `[DEFAULT]` -section, but not change the name _DEFAULT_. It is essential to the right working -of the program, due to the other sections inherit properties from it. - -You can read `man 5 snapman` to a complete reference of configuration file -format. - -We'll go to show a little example. In it we make snapshots for the root -filesystem and the `/home` directory. - - $ cat /etc/snapman.ini - [DEFAULT] - subvolume = / - frequency = 1d - quota = 30 - readonly = True - enabled = True - timestamp = %Y%m%d-%H%M%S - umask = 022 - - [/snapshots] - - [/home/snapshots] - subvolume = /home - -The section name (the path inter `[]`) is the directory where snapshots will be -stored. - -Each section name has some values. There is a section, named `[DEFAULT]`. This -is used for other sections for get default values, if they are not configured -in itself. - -### Command line mode - - -Execute as root: - - # snapman - -The program will create two snapshots. One for each subvolume especified -in every section: - - $ ls /snapshots /home/snapshots - /snapshots: - 20171025-084416 - - /home/snapshots: - 20171025-084416 - -In the case of `[/snapshots]` section the subvolume option was inherit from -`[DEFAULT]` section. - -If we execute again `snapman` no action will be runned, due to the frequency -option is `1d` (one day). Only when the difference is greater than one day -would make a new snapshot. - -When the number of snapshots is greater than the specified by `quota` option -the older snapshot will be deleted and a new one will be created. - -The `readonly` option makes readonly snapshots and the `enabled` option allows -to disable this section, without delete snapshots. Both options accept boolean -values: True, False, 0, 1, on, off. - -The `timestamp` option is the name of snapshot. -See -for a complete documentation of this format. - -The `umask` value set the umask value for directory creation. - -### Graphic mode - -Execute as root: - - # snapman --gui - -Or search the application in your desktop menu. - -### Daemon mode - -Rather than executing manually every certain time, we can use the `--daemon` -command line option: - - # snapman --daemon - -And then the program will make copies when necessary to meet the indicated -frequency. - -For that, a systemd service is included. Just start and enable it: - - # systemctl start snapman - # systemctl enable snapman - -Or, if you do no use systemd, add a line like `snapman --daemon` to your -`/etc/rc.local` or whatever it is that uses your startup system. - - -### Command line options. - -Snapman program allows to manage snapshots by command line. Here some examples. - -To show all command line options: - - $ snapman --help - -A list of managed sections: - - $ snapman --sections - -A list of snapshots of a section: - - $ snapman --section-snapshots /home/snapshots - -Show information of a section: - - $ snapman --section-info /home/snapshots - -Delete all snapshots of a section: - - # snapman --section-clean /home/snapshots - -Force creation of a new snapshot for section: - - # snapman --section-newsnapshot /home/snapshots - -To show additional information when performing actions use `--verbose` option. - -There are more command line options. Please refer to `man snapman` for a -complete and updated guide. - -Installation ------------- - -You can choose between different installation methods. - -### Classic method ### - -- Build and install: - - $ make - # make install - -- Uninstall: - - # make uninstall - - -### Debian package ### - -- Build and install: - - $ make debian_pkg - # dpkg -i snapman_1.0a_all.deb - -- Uninstall: - - # apt purge snapman - - -### Arch Linux package - -- Build and install: - - $ make arch_pkg - # pacman -U snapman-1.0a-1--any.pkg.tar.xz - -- Uninstall: - - # pacman -Rsc snapman diff --git a/man/en/snapman.1.rst b/man/en/snapman.1.rst index aab3015..77831eb 100644 --- a/man/en/snapman.1.rst +++ b/man/en/snapman.1.rst @@ -26,7 +26,7 @@ DESCRIPTION system to capture snapshots of subvolumes. When you run ``snapman`` without options it will read the default -configuration file in ``/etc/snapman.ini`` and then it will make backups +configuration file in ``/etc/snapman.ini`` (see snapman(5)) and then it will make backups (snapshots) of indicated subvolumes at desired frequency until reach a defined quota. If quota were reached, then it will remove the older backup before to make a new one to keep the number of backups indicated diff --git a/src/Snapman/about.py b/src/Snapman/about.py index 3246e2d..d924c0d 100644 --- a/src/Snapman/about.py +++ b/src/Snapman/about.py @@ -148,59 +148,3 @@ def __init__(self, parent=None): mainLayout.addWidget(mail1) mainLayout.addStretch(1) self.setLayout(mainLayout) - -class HtmlHelp(QWidget): - def __init__(self, parent=None): - super(HtmlHelp, self).__init__(parent) - self.setWindowIcon(QIcon.fromTheme('help-about')) - - #self.view = QWebEngineView() - - btn1 = QPushButton(_("Application"), self) - btn1.setIcon(QIcon.fromTheme("help-about")) - btn1.setToolTip(_("Show application help")) - #btn1.clicked.connect(self.loadHtml1) - - btn5 = QPushButton(_("Configuration"), self) - btn5.setIcon(QIcon.fromTheme("help-about")) - btn5.setToolTip(_("Show configuration help")) - #btn5.clicked.connect(self.loadHtml5) - - btnUsage = QPushButton(_("Usage"), self) - btnUsage.setIcon(QIcon.fromTheme("help-about")) - btnUsage.setToolTip(_("Show usage help")) - #btnUsage.clicked.connect(self.loadHtmlUsage) - - mainLayout =QGridLayout() - mainLayout.addWidget(btn1, 0, 0) - mainLayout.addWidget(btn5, 0, 1) - mainLayout.addWidget(btnUsage, 0, 2) - #mainLayout.addWidget(self.view, 1, 0, 1, 3) - self.setLayout(mainLayout) - #self.loadHtml1() -''' - def loadHtml1(self): - print('load') - f = open(os.path.join(docpath, 'snapman.1.html'), 'r') - html = f.read() - f.close() - self.view.setHtml(html) - self.view.show() - self.setWindowTitle(PROGRAM_NAME + _(": Application help")) - - def loadHtml5(self): - f = open(os.path.join(docpath, 'snapman.5.html'), 'r') - html = f.read() - f.close() - self.view.setHtml(html) - self.view.show() - self.setWindowTitle(PROGRAM_NAME + _(": Configuration help")) - - def loadHtmlUsage(self): - f = open(os.path.join(docpath, 'USAGE.html'), 'r') - html = f.read() - f.close() - self.view.setHtml(html) - self.view.show() - self.setWindowTitle(PROGRAM_NAME + _(": Usage help")) -''' diff --git a/src/Snapman/about.py-qwebkit.noworkasroot b/src/Snapman/about.py-qwebkit.noworkasroot new file mode 100644 index 0000000..3246e2d --- /dev/null +++ b/src/Snapman/about.py-qwebkit.noworkasroot @@ -0,0 +1,206 @@ +import os + +from PyQt5.QtWidgets import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWebEngineWidgets import * + +from Snapman.paths import docpath +from Snapman.program_info import * + + +class AboutDialog(QWidget): + def __init__(self, parent=None): + super(AboutDialog, self).__init__(parent) + + font = QFont() + font.setPointSize(18) + font.setBold(False) + labelIcon = QLabel() + pixmap = QIcon.fromTheme(EXECUTABLE_NAME).pixmap(QSize(64, 64)) + labelIcon.setPixmap(pixmap) + labelText = QLabel(PROGRAM_NAME) + labelText.setFont(font) + + tabWidget = QTabWidget() + tabWidget.addTab(AboutTab(), _("About")) + tabWidget.addTab(VersionTab(), _("Version")) + tabWidget.addTab(AuthorsTab(), _("Authors")) + tabWidget.addTab(ThanksTab(), _("Thanks")) + tabWidget.addTab(TranslationTab(), _("Translation")) + + btn = QPushButton(_("Close"), self) + btn.setIcon(QIcon.fromTheme("window-close")) + btn.setToolTip(_("Close this window")) + btn.clicked.connect(self.close) + + labelLayout = QHBoxLayout() + labelLayout.addWidget(labelIcon) + labelLayout.addWidget(labelText, Qt.AlignLeft) + + mainLayout = QGridLayout() + mainLayout.addLayout(labelLayout, 0, 0) + mainLayout.addWidget(tabWidget, 1, 0) + mainLayout.addWidget(btn, 2, 0, Qt.AlignRight) + self.setLayout(mainLayout) + + self.setWindowTitle(_("About") + " " + PROGRAM_NAME) + self.setWindowIcon(QIcon.fromTheme(EXECUTABLE_NAME)) + + +class AboutTab(QWidget): + def __init__(self, parent=None): + super(AboutTab, self).__init__(parent) + + blank = QLabel() + description = QLabel(DESCRIPTION) + copyright = QLabel("© 2018, " + AUTHOR) + source = QLabel(_("Source:") + " " + + "" + SOURCE + "") + license = QLabel(_("License:") + " " + + "" + + _("GNU General Public License, version 3") + "") + + source.setTextInteractionFlags(Qt.TextBrowserInteraction) + + mainLayout = QVBoxLayout() + mainLayout.addWidget(blank) + mainLayout.addWidget(blank) + mainLayout.addWidget(blank) + mainLayout.addWidget(description) + mainLayout.addWidget(blank) + mainLayout.addWidget(copyright) + mainLayout.addWidget(source) + mainLayout.addWidget(license) + mainLayout.addStretch() + self.setLayout(mainLayout) + + +class VersionTab(QWidget): + def __init__(self, parent=None): + super(VersionTab, self).__init__(parent) + + version = QLabel("" + _("Version") + " " + VERSION + "") + using = QLabel(_("Using:") + " ") + python = QLabel("
  • Python " + PYVER) + pyqt = QLabel("
    • PyQt " + QT_VERSION_STR) + + mainLayout = QVBoxLayout() + mainLayout.addWidget(version) + mainLayout.addWidget(using) + mainLayout.addWidget(python) + mainLayout.addWidget(pyqt) + mainLayout.addStretch(1) + self.setLayout(mainLayout) + + +class AuthorsTab(QWidget): + def __init__(self, parent=None): + super(AuthorsTab, self).__init__(parent) + + blank = QLabel() + notice = QLabel(_("Mail me if you found bugs.")) + name1 = QLabel("" + AUTHOR + "") + task1 = QLabel("" + _("Principle author") + "") + mail1 = QLabel("
      " + MAIL + "
      ") + + mainLayout = QVBoxLayout() + mainLayout.addWidget(notice) + mainLayout.addWidget(blank) + mainLayout.addWidget(name1) + mainLayout.addWidget(task1) + mainLayout.addWidget(mail1) + mainLayout.addStretch(1) + self.setLayout(mainLayout) + + +class ThanksTab(QWidget): + def __init__(self, parent=None): + super(ThanksTab, self).__init__(parent) + + blank = QLabel() + notice = QLabel(_("Thank you for using my program.")) + + mainLayout = QVBoxLayout() + mainLayout.addWidget(blank) + mainLayout.addWidget(notice) + mainLayout.addStretch(1) + self.setLayout(mainLayout) + + +class TranslationTab(QWidget): + def __init__(self, parent=None): + super(TranslationTab, self).__init__(parent) + + blank = QLabel() + notice = QLabel(_("Please, mail me if you want to") + " " + + _("improve the translation.")) + name1 = QLabel("" + AUTHOR + "") + task1 = QLabel("" + _("Spanish and english translation") + "") + mail1 = QLabel("
      " + MAIL + "
      ") + + mainLayout = QVBoxLayout() + mainLayout.addWidget(notice) + mainLayout.addWidget(blank) + mainLayout.addWidget(name1) + mainLayout.addWidget(task1) + mainLayout.addWidget(mail1) + mainLayout.addStretch(1) + self.setLayout(mainLayout) + +class HtmlHelp(QWidget): + def __init__(self, parent=None): + super(HtmlHelp, self).__init__(parent) + self.setWindowIcon(QIcon.fromTheme('help-about')) + + #self.view = QWebEngineView() + + btn1 = QPushButton(_("Application"), self) + btn1.setIcon(QIcon.fromTheme("help-about")) + btn1.setToolTip(_("Show application help")) + #btn1.clicked.connect(self.loadHtml1) + + btn5 = QPushButton(_("Configuration"), self) + btn5.setIcon(QIcon.fromTheme("help-about")) + btn5.setToolTip(_("Show configuration help")) + #btn5.clicked.connect(self.loadHtml5) + + btnUsage = QPushButton(_("Usage"), self) + btnUsage.setIcon(QIcon.fromTheme("help-about")) + btnUsage.setToolTip(_("Show usage help")) + #btnUsage.clicked.connect(self.loadHtmlUsage) + + mainLayout =QGridLayout() + mainLayout.addWidget(btn1, 0, 0) + mainLayout.addWidget(btn5, 0, 1) + mainLayout.addWidget(btnUsage, 0, 2) + #mainLayout.addWidget(self.view, 1, 0, 1, 3) + self.setLayout(mainLayout) + #self.loadHtml1() +''' + def loadHtml1(self): + print('load') + f = open(os.path.join(docpath, 'snapman.1.html'), 'r') + html = f.read() + f.close() + self.view.setHtml(html) + self.view.show() + self.setWindowTitle(PROGRAM_NAME + _(": Application help")) + + def loadHtml5(self): + f = open(os.path.join(docpath, 'snapman.5.html'), 'r') + html = f.read() + f.close() + self.view.setHtml(html) + self.view.show() + self.setWindowTitle(PROGRAM_NAME + _(": Configuration help")) + + def loadHtmlUsage(self): + f = open(os.path.join(docpath, 'USAGE.html'), 'r') + html = f.read() + f.close() + self.view.setHtml(html) + self.view.show() + self.setWindowTitle(PROGRAM_NAME + _(": Usage help")) +''' diff --git a/src/Snapman/gui.py b/src/Snapman/gui.py index 7e9f9a1..5b7794d 100644 --- a/src/Snapman/gui.py +++ b/src/Snapman/gui.py @@ -1,4 +1,5 @@ import time +import subprocess from Snapman.section import Section, Snapshot, settings from Snapman.about import * from Snapman.quit import * # include os, sys @@ -260,7 +261,7 @@ def __init__(self): self.snapshotTable = SnapshotTable() self.statusPathFocused = QLabel() self.aboutDialog = AboutDialog() - self.helpView = HtmlHelp() + #self.helpView = HtmlHelp() self.createActions() self.createMenus() @@ -299,7 +300,9 @@ def showAbout(self): self.aboutDialog.show() def showHelp(self): - self.helpView.show() + cmd = ('khelpcenter', 'man:snapman(1)') + subprocess.run(cmd) + #self.helpView.show() def updateButtons(self): if len(self.sectionTable.selected) == 0: @@ -447,4 +450,12 @@ def run(): app = QApplication(sys.argv) +app.setOrganizationName(PROGRAM_NAME + ' project') +app.setApplicationName(PROGRAM_NAME) +app.setApplicationDisplayName(PROGRAM_NAME) +app.setApplicationVersion(VERSION) +app.setOrganizationDomain(AUTHOR + '.me') +if hasattr(app, 'setDesktopFileName'): # available since Qt 5.7 + app.setDesktopFileName('me.' + AUTHOR + '.' + PROGRAM_NAME + '.desktop') + mainWindow = MainWindow() diff --git a/src/Snapman/section.py b/src/Snapman/section.py index e5241dd..e35463a 100644 --- a/src/Snapman/section.py +++ b/src/Snapman/section.py @@ -46,34 +46,31 @@ class Section(): info_umask = 'umask' def __init__(self, section): + self.noaccess = False + + self.name = section.name # The name of section in configfile (a path). self.subvolume = section['subvolume'] self.frequency = section['frequency'] - self.frequency_sec = self.toseconds(self.frequency) self.quota = section.getint('quota') self.readonly = section.getboolean('readonly') self.enabled = section.getboolean('enabled') self.timestamp = section['timestamp'] self.umask = section['umask'] - - self.name = section.name # The name of section in configfile (a path). - - self.noaccess = False - self.snapshot_list() # Changes self.noaccess value - self.nsnapshots = self.nsnapshots() - self.count = self.count() - self.percent = self.percent() - + if args.verbose: self.stdout = subprocess.STDOUT self.stderr = subprocess.STDOUT else: self.stdout = subprocess.DEVNULL self.stderr = subprocess.DEVNULL + + self.snapshot_list() # Changes self.noaccess value + self.getFrequency_sec() # Gets self.frequency_sec - def toseconds(self, period): + def getFrequency_sec(self): '''Read minutes, hours, days or years and converts it to seconds.''' - period = str(period) + period = str(self.frequency) if period.endswith('s'): fperiod = period.replace('s', '') elif period.endswith('m'): @@ -101,10 +98,18 @@ def toseconds(self, period): elif period.endswith('y'): fperiod = fperiod * 60 * 60 * 24 * 365 - return(fperiod) + self.frequency_sec = fperiod def snapshot_list(self): - ''' Return a list of snapshots or a empty list ''' + ''' + Sets: + self.noaccess + self.snapshots + self.snapshot_paths + self.nsnapshots + self.count + self.percent + ''' try: self.snapshots = os.listdir(self.name) except FileNotFoundError: @@ -116,6 +121,9 @@ def snapshot_list(self): file=sys.stderr) self.snapshot_paths = list(map(lambda x: os.path.join(self.name, x), self.snapshots)) + self.nsnapshots = self.nsnapshots() + self.count = self.count() + self.percent = self.percent() def count(self): return(str(self.nsnapshots) + ' / ' + str(self.quota)) @@ -328,6 +336,7 @@ def start(self): if args.verbose: print("Snapshotting", self.name, "every", self.frequency_sec, "seconds") + # self.reload() # Re-read configfile for update properties self.makesnapshot() time.sleep(self.frequency_sec)