mirror of https://github.com/borgbase/vorta
Refactor into modular widgets.
This commit is contained in:
parent
03f6286d7d
commit
5906a4b0c2
|
@ -1,149 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AddRepository</class>
|
||||
<widget class="QDialog" name="AddRepository">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>250</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="formLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>20</y>
|
||||
<width>591</width>
|
||||
<height>271</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Repository URL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="repoURL">
|
||||
<property name="placeholderText">
|
||||
<string>csvis8xq@csvis8xq.repo.borgbase.com:repo</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="encryptionLabel">
|
||||
<property name="text">
|
||||
<string>Encryption:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="encryptionComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Select Encryption Mode</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="saveButton">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>SSH Key:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="sshComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Automatically choose SSH Key (default)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="errorText">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="passwordLineEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -1,66 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>601</width>
|
||||
<height>301</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="logText">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Logging output</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -54,390 +54,28 @@
|
|||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<widget class="QWidget" name="repoTabSlot">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Repository</string>
|
||||
</attribute>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>351</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remote repository. The place to store your backups.</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="formLayoutWidget_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>240</y>
|
||||
<width>771</width>
|
||||
<height>171</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>25</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Encryption:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="repoEncryption">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="Line" name="line">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Original Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="sizeOriginal">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>Deduplicated Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Compressed Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="sizeCompressed">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="sizeDeduplicated">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>30</y>
|
||||
<width>769</width>
|
||||
<height>197</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>25</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Repository:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="repoSelector">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Select Backup Destination</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Need hosting for your Borg repository? <a href="https://www.borgbackup.org/support/commercial.html"><span style=" text-decoration: underline; color:#0000ff;">View providers</span></a>.</p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string>SSH Key:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="sshComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Automatically choose SSH Key (default)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Create New Key</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Compression:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="repoCompression"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>To access repository securely.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab">
|
||||
<widget class="QWidget" name="sourceTabSlot">
|
||||
<attribute name="title">
|
||||
<string>Sources</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>40</y>
|
||||
<width>721</width>
|
||||
<height>371</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="sourceDirectoriesWidget"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="sourceAdd">
|
||||
<property name="text">
|
||||
<string>Add Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QPushButton" name="sourceRemove">
|
||||
<property name="text">
|
||||
<string>Remove Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>10</y>
|
||||
<width>361</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Choose the folders to back up.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<widget class="QWidget" name="snapshotTabSlot">
|
||||
<attribute name="title">
|
||||
<string>Snapshots</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="verticalLayoutWidget_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>-10</x>
|
||||
<y>0</y>
|
||||
<width>791</width>
|
||||
<height>401</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="snapshotTable">
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Date</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="mountErrors">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>To mount snapshots, first install &quot;FUSE for macOS&quot; from <a href="https://osxfuse.github.io/"><span style=" text-decoration: underline; color:#0000ff;">here</span></a>.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QPushButton" name="snapshotMountButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mount</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="snapshotDeleteButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignLeft">
|
||||
<widget class="QPushButton" name="snapshotRefreshButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AddRepository</class>
|
||||
<widget class="QDialog" name="AddRepository">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>250</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Repository URL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="repoURL">
|
||||
<property name="placeholderText">
|
||||
<string>csvis8xq@csvis8xq.repo.borgbase.com:repo</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="encryptionLabel">
|
||||
<property name="text">
|
||||
<string>Encryption:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="encryptionComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Select Encryption Mode</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="saveButton">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>SSH Key:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="sshComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Automatically choose SSH Key (default)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="errorText">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="passwordLineEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,233 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>831</width>
|
||||
<height>461</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Ignored" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remote repository. The place to store your backups.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>25</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Repository:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="repoSelector">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Select Backup Destination</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Need hosting for your Borg repository? <a href="https://www.borgbackup.org/support/commercial.html"><span style=" text-decoration: underline; color:#0000ff;">View providers</span></a>.</p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string>SSH Key:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="sshComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Automatically choose SSH Key (default)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Create New Key</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Compression:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="repoCompression"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>To access repository securely.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
|
||||
</property>
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>25</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Encryption:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="repoEncryption">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="Line" name="line">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Original Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="sizeOriginal">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>Deduplicated Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Compressed Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="sizeCompressed">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="sizeDeduplicated">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>799</width>
|
||||
<height>514</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="snapshotTable">
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Date</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="mountErrors">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>To mount snapshots, first install &quot;FUSE for macOS&quot; from <a href="https://osxfuse.github.io/"><span style=" text-decoration: underline; color:#0000ff;">here</span></a>.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QPushButton" name="snapshotMountButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mount</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="snapshotDeleteButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignLeft">
|
||||
<widget class="QPushButton" name="snapshotRefreshButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>791</width>
|
||||
<height>497</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Choose the folders to back up.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="sourceDirectoriesWidget"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="sourceAdd">
|
||||
<property name="text">
|
||||
<string>Add Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QPushButton" name="sourceRemove">
|
||||
<property name="text">
|
||||
<string>Remove Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -9,5 +9,5 @@ SETTINGS_DIR = appdirs.user_data_dir(APP_NAME, APP_AUTHOR)
|
|||
if not os.path.exists(SETTINGS_DIR):
|
||||
os.makedirs(SETTINGS_DIR)
|
||||
|
||||
def reset_app():
|
||||
def remove_config():
|
||||
shutil.rmtree(SETTINGS_DIR)
|
||||
|
|
228
vorta/main.py
228
vorta/main.py
|
@ -2,16 +2,15 @@ import os
|
|||
import platform
|
||||
from datetime import datetime as dt
|
||||
from dateutil import parser
|
||||
from PyQt5.QtWidgets import QApplication, QFileDialog, QTableWidgetItem, QTableView, QTableWidget
|
||||
from PyQt5 import uic, QtCore, QtGui, QtWidgets
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5 import uic
|
||||
|
||||
from .repo_add import AddRepoWindow, ExistingRepoWindow
|
||||
from .ssh_add import SSHAddWindow
|
||||
from .config import APP_NAME, reset_app
|
||||
from .models import RepoModel, SourceDirModel, SnapshotModel, BackupProfileModel
|
||||
from .ssh_keys import get_private_keys
|
||||
from .config import APP_NAME, remove_config
|
||||
from .models import SnapshotModel, BackupProfileModel, SourceDirModel
|
||||
from .borg_runner import BorgThread
|
||||
from .utils import prettyByes
|
||||
from .repo_tab import RepoTab
|
||||
from .source_tab import SourceTab
|
||||
from .snapshots_tab import SnapshotTab
|
||||
|
||||
|
||||
uifile = os.path.join(os.path.dirname(__file__), 'UI/mainwindow.ui')
|
||||
|
@ -24,11 +23,12 @@ class MainWindow(MainWindowBase, MainWindowUI):
|
|||
self.setupUi(self)
|
||||
self.setWindowTitle(APP_NAME)
|
||||
self.profile = BackupProfileModel.get(id=1)
|
||||
self.init_repo()
|
||||
self.init_source()
|
||||
self.init_snapshots()
|
||||
self.init_compression()
|
||||
self.init_ssh()
|
||||
|
||||
self.repoTab = RepoTab(self.repoTabSlot)
|
||||
self.repoTab.repo_changed.connect(lambda: print('adf'))
|
||||
|
||||
self.sourceTab = SourceTab(self.sourceTabSlot)
|
||||
self.snapshotTab = SnapshotTab(self.snapshotTabSlot)
|
||||
|
||||
self.createStartBtn.clicked.connect(self.create_action)
|
||||
self.actionResetApp.triggered.connect(self.menu_reset)
|
||||
|
@ -41,7 +41,7 @@ class MainWindow(MainWindowBase, MainWindowUI):
|
|||
self.createProgressText.repaint()
|
||||
|
||||
def create_action(self):
|
||||
n_backup_folders = self.sourceDirectoriesWidget.count()
|
||||
n_backup_folders = SourceDirModel.select().count()
|
||||
if n_backup_folders == 0:
|
||||
self.set_status('Add some folders to back up first.')
|
||||
return
|
||||
|
@ -53,8 +53,8 @@ class MainWindow(MainWindowBase, MainWindowUI):
|
|||
cmd = ['borg', 'create', '--list', '--info', '--log-json', '--json', '-C', self.profile.compression,
|
||||
f'{repo.url}::{platform.node()}-{dt.now().isoformat()}'
|
||||
]
|
||||
for i in range(n_backup_folders):
|
||||
cmd.append(self.sourceDirectoriesWidget.item(i).text())
|
||||
for f in SourceDirModel.select():
|
||||
cmd.append(f.dir)
|
||||
|
||||
thread = BorgThread(self, cmd, {})
|
||||
thread.updated.connect(self.create_update_log)
|
||||
|
@ -84,200 +84,8 @@ class MainWindow(MainWindowBase, MainWindowUI):
|
|||
repo.unique_size = stats['unique_size']
|
||||
repo.total_unique_chunks = stats['total_unique_chunks']
|
||||
repo.save()
|
||||
self.init_snapshots()
|
||||
|
||||
def init_source(self):
|
||||
self.sourceAdd.clicked.connect(self.source_add)
|
||||
self.sourceRemove.clicked.connect(self.source_remove)
|
||||
for source in SourceDirModel.select():
|
||||
self.sourceDirectoriesWidget.addItem(source.dir)
|
||||
|
||||
def init_ssh(self):
|
||||
keys = get_private_keys()
|
||||
for key in keys:
|
||||
self.sshComboBox.addItem(f'{key["filename"]} ({key["format"]}:{key["fingerprint"]})', key['filename'])
|
||||
self.sshComboBox.currentIndexChanged.connect(self.ssh_select_action)
|
||||
|
||||
def ssh_select_action(self, index):
|
||||
if index == 1:
|
||||
ssh_add_window = SSHAddWindow()
|
||||
ssh_add_window.setParent(self, QtCore.Qt.Sheet)
|
||||
ssh_add_window.show()
|
||||
if ssh_add_window.exec_():
|
||||
self.init_ssh()
|
||||
else:
|
||||
self.profile.ssh_key = self.sshComboBox.itemData(index)
|
||||
self.profile.save()
|
||||
print('set ssh key to', self.profile.ssh_key)
|
||||
|
||||
def init_snapshots(self):
|
||||
if self.profile.repo:
|
||||
snapshots = [s for s in self.profile.repo.snapshots.select()]
|
||||
|
||||
for row, snapshot in enumerate(snapshots):
|
||||
self.snapshotTable.insertRow(row)
|
||||
self.snapshotTable.setItem(row, 0, QTableWidgetItem(snapshot.name))
|
||||
formatted_time = snapshot.time.strftime('%Y-%m-%d %H:%M')
|
||||
self.snapshotTable.setItem(row, 1, QTableWidgetItem(formatted_time))
|
||||
|
||||
self.sizeCompressed.setText(prettyByes(self.profile.repo.unique_csize))
|
||||
self.sizeDeduplicated.setText(prettyByes(self.profile.repo.unique_size))
|
||||
self.sizeOriginal.setText(prettyByes(self.profile.repo.total_size))
|
||||
self.repoEncryption.setText(str(self.profile.repo.encryption))
|
||||
self.snapshotTable.setRowCount(len(snapshots))
|
||||
|
||||
header = self.snapshotTable.horizontalHeader()
|
||||
header.setVisible(True)
|
||||
header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
||||
header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
|
||||
|
||||
self.snapshotTable.setSelectionBehavior(QTableView.SelectRows)
|
||||
self.snapshotTable.setEditTriggers(QTableView.NoEditTriggers)
|
||||
|
||||
self.snapshotMountButton.clicked.connect(self.snapshot_mount)
|
||||
self.snapshotDeleteButton.clicked.connect(self.snapshot_mount)
|
||||
self.snapshotRefreshButton.clicked.connect(self.snapshot_mount)
|
||||
|
||||
def snapshot_mount(self):
|
||||
cmd = ['borg', 'mount', '--log-json']
|
||||
row_selected = self.snapshotTable.selectionModel().selectedRows()
|
||||
if row_selected:
|
||||
snapshot_cell = self.snapshotTable.item(row_selected[0].row(), 0)
|
||||
if snapshot_cell:
|
||||
snapshot_name = snapshot_cell.text()
|
||||
cmd.append(f'{self.profile.repo.url}::{snapshot_name}')
|
||||
else:
|
||||
cmd.append(f'{self.profile.repo.url}')
|
||||
else:
|
||||
cmd.append(f'{self.profile.repo.url}')
|
||||
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.ShowDirsOnly
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
mountPoint = QFileDialog.getExistingDirectory(
|
||||
self, "Choose Mount Point", "", options=options)
|
||||
if mountPoint:
|
||||
cmd.append(mountPoint)
|
||||
|
||||
self.set_status('Mounting snapshot into folder', 0)
|
||||
thread = BorgThread(self, cmd, {})
|
||||
thread.updated.connect(self.mount_update_log)
|
||||
thread.result.connect(self.mount_get_result)
|
||||
thread.start()
|
||||
|
||||
def mount_update_log(self, text):
|
||||
self.mountErrors.setText(text)
|
||||
|
||||
def mount_get_result(self, result):
|
||||
self.set_status(progress_max=100)
|
||||
if result['returncode'] == 0:
|
||||
self.set_status('Mounted successfully.')
|
||||
|
||||
def init_compression(self):
|
||||
self.repoCompression.addItem('LZ4 (default)', 'lz4')
|
||||
self.repoCompression.addItem('Zstandard (medium)', 'zstd')
|
||||
self.repoCompression.addItem('LZMA (high)', 'lzma,6')
|
||||
self.repoCompression.setCurrentIndex(self.repoCompression.findData(self.profile.compression))
|
||||
self.repoCompression.currentIndexChanged.connect(self.compression_select_action)
|
||||
|
||||
def compression_select_action(self, index):
|
||||
self.profile.compression = self.repoCompression.currentData()
|
||||
self.profile.save()
|
||||
|
||||
def init_repo(self):
|
||||
self.repoSelector.model().item(0).setEnabled(False)
|
||||
self.repoSelector.addItem('Initialize New Repository', 'init')
|
||||
self.repoSelector.addItem('Add Existing Repository', 'existing')
|
||||
for repo in RepoModel.select():
|
||||
self.repoSelector.addItem(repo.url, repo.id)
|
||||
|
||||
if self.profile.repo:
|
||||
self.repoSelector.setCurrentIndex(self.repoSelector.findData(self.profile.repo.id))
|
||||
self.repoSelector.currentIndexChanged.connect(self.repo_select_action)
|
||||
|
||||
def repo_select_action(self, index):
|
||||
if index <= 2:
|
||||
if index == 1:
|
||||
repo_add_window = AddRepoWindow()
|
||||
else:
|
||||
repo_add_window = ExistingRepoWindow()
|
||||
|
||||
repo_add_window.setParent(self, QtCore.Qt.Sheet)
|
||||
repo_add_window.show()
|
||||
if repo_add_window.exec_():
|
||||
params = repo_add_window.get_values()
|
||||
|
||||
if index == 1:
|
||||
cmd = ["borg", "init", "--log-json", f"--encryption={params['encryption']}", params['repo_url']]
|
||||
else:
|
||||
cmd = ["borg", "list", "--json", params['repo_url']]
|
||||
|
||||
self.set_status('Connecting to repo...', 0)
|
||||
thread = BorgThread(self, cmd, params)
|
||||
thread.updated.connect(self.repo_add_update_log)
|
||||
thread.result.connect(self.repo_add_result)
|
||||
thread.start()
|
||||
else:
|
||||
self.profile.repo = self.repoSelector.currentData()
|
||||
self.profile.save()
|
||||
self.init_snapshots()
|
||||
|
||||
def repo_add_update_log(self, text):
|
||||
self.set_status(text)
|
||||
|
||||
def repo_add_result(self, result):
|
||||
if result['returncode'] == 0:
|
||||
self.set_status('Successfully connected to repo.', 100)
|
||||
new_repo, _ = RepoModel.get_or_create(
|
||||
url=result['params']['repo_url'],
|
||||
defaults={
|
||||
'password': result['params']['password'],
|
||||
# 'encryption': result['params'].get('encryption', '')
|
||||
}
|
||||
)
|
||||
if 'cache' in result['data']:
|
||||
stats = result['data']['cache']['stats']
|
||||
new_repo.total_size = stats['total_size']
|
||||
new_repo.unique_csize = stats['unique_csize']
|
||||
new_repo.unique_size = stats['unique_size']
|
||||
new_repo.total_unique_chunks = stats['total_unique_chunks']
|
||||
new_repo.encryption = result['data']['encryption']['mode']
|
||||
new_repo.save()
|
||||
self.profile.repo = new_repo.id
|
||||
self.profile.save()
|
||||
|
||||
if 'archives' in result['data'].keys():
|
||||
for snapshot in result['data']['archives']:
|
||||
new_snapshot, _ = SnapshotModel.get_or_create(
|
||||
snapshot_id=snapshot['id'],
|
||||
defaults={
|
||||
'repo': new_repo.id,
|
||||
'name': snapshot['name'],
|
||||
'time': parser.parse(snapshot['time'])
|
||||
}
|
||||
)
|
||||
new_snapshot.save()
|
||||
self.repoSelector.addItem(new_repo.url, new_repo.id)
|
||||
self.repoSelector.setCurrentIndex(self.repoSelector.count()-1)
|
||||
self.init_snapshots()
|
||||
|
||||
def source_add(self):
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.ShowDirsOnly
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
fileName = QFileDialog.getExistingDirectory(
|
||||
self, "Choose Backup Directory", "", options=options)
|
||||
if fileName:
|
||||
self.sourceDirectoriesWidget.addItem(fileName)
|
||||
new_source = SourceDirModel(dir=fileName)
|
||||
new_source.save()
|
||||
|
||||
def source_remove(self):
|
||||
item = self.sourceDirectoriesWidget.takeItem(self.sourceDirectoriesWidget.currentRow())
|
||||
db_item = SourceDirModel.get(dir=item.text())
|
||||
db_item.delete_instance()
|
||||
item = None
|
||||
self.snapshotTab.populate()
|
||||
|
||||
def menu_reset(self):
|
||||
reset_app()
|
||||
remove_config()
|
||||
QApplication.instance().quit()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
from PyQt5 import uic
|
||||
from .ssh_keys import get_private_keys
|
||||
from .utils import get_private_keys
|
||||
|
||||
uifile = os.path.join(os.path.dirname(__file__), 'UI/addrepo.ui')
|
||||
uifile = os.path.join(os.path.dirname(__file__), 'UI/repoadd.ui')
|
||||
AddRepoUI, AddRepoBase = uic.loadUiType(uifile)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
import os
|
||||
from dateutil import parser
|
||||
from PyQt5 import uic, QtCore
|
||||
|
||||
from .models import RepoModel, SourceDirModel, SnapshotModel, BackupProfileModel
|
||||
from .repo_add import AddRepoWindow, ExistingRepoWindow
|
||||
from .borg_runner import BorgThread
|
||||
from .utils import prettyBytes, get_private_keys
|
||||
from .ssh_add import SSHAddWindow
|
||||
|
||||
uifile = os.path.join(os.path.dirname(__file__), 'UI/repotab.ui')
|
||||
RepoUI, RepoBase = uic.loadUiType(uifile)
|
||||
|
||||
|
||||
class RepoTab(RepoBase, RepoUI):
|
||||
repo_changed = QtCore.pyqtSignal(int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(parent)
|
||||
self.profile = self.window().profile
|
||||
|
||||
self.repoSelector.model().item(0).setEnabled(False)
|
||||
self.repoSelector.addItem('Initialize New Repository', 'init')
|
||||
self.repoSelector.addItem('Add Existing Repository', 'existing')
|
||||
for repo in RepoModel.select():
|
||||
self.repoSelector.addItem(repo.url, repo.id)
|
||||
|
||||
if self.profile.repo:
|
||||
self.repoSelector.setCurrentIndex(self.repoSelector.findData(self.profile.repo.id))
|
||||
self.repoSelector.currentIndexChanged.connect(self.repo_select_action)
|
||||
|
||||
self.repoCompression.addItem('LZ4 (default)', 'lz4')
|
||||
self.repoCompression.addItem('Zstandard (medium)', 'zstd')
|
||||
self.repoCompression.addItem('LZMA (high)', 'lzma,6')
|
||||
self.repoCompression.setCurrentIndex(self.repoCompression.findData(self.profile.compression))
|
||||
self.repoCompression.currentIndexChanged.connect(self.compression_select_action)
|
||||
|
||||
self.init_ssh()
|
||||
self.init_repo_stats()
|
||||
|
||||
def init_repo_stats(self):
|
||||
self.sizeCompressed.setText(prettyBytes(self.profile.repo.unique_csize))
|
||||
self.sizeDeduplicated.setText(prettyBytes(self.profile.repo.unique_size))
|
||||
self.sizeOriginal.setText(prettyBytes(self.profile.repo.total_size))
|
||||
self.repoEncryption.setText(str(self.profile.repo.encryption))
|
||||
self.repo_changed.emit(self.profile.repo.id)
|
||||
|
||||
def init_ssh(self):
|
||||
keys = get_private_keys()
|
||||
for key in keys:
|
||||
self.sshComboBox.addItem(f'{key["filename"]} ({key["format"]}:{key["fingerprint"]})', key['filename'])
|
||||
self.sshComboBox.currentIndexChanged.connect(self.ssh_select_action)
|
||||
|
||||
def ssh_select_action(self, index):
|
||||
if index == 1:
|
||||
ssh_add_window = SSHAddWindow()
|
||||
ssh_add_window.setParent(self, QtCore.Qt.Sheet)
|
||||
ssh_add_window.show()
|
||||
if ssh_add_window.exec_():
|
||||
self.init_ssh()
|
||||
else:
|
||||
self.profile.ssh_key = self.sshComboBox.itemData(index)
|
||||
self.profile.save()
|
||||
print('set ssh key to', self.profile.ssh_key)
|
||||
|
||||
|
||||
def compression_select_action(self, index):
|
||||
self.profile.compression = self.repoCompression.currentData()
|
||||
self.profile.save()
|
||||
|
||||
def repo_select_action(self, index):
|
||||
if index <= 2:
|
||||
if index == 1:
|
||||
repo_add_window = AddRepoWindow()
|
||||
else:
|
||||
repo_add_window = ExistingRepoWindow()
|
||||
|
||||
repo_add_window.setParent(self, QtCore.Qt.Sheet)
|
||||
repo_add_window.show()
|
||||
if repo_add_window.exec_():
|
||||
params = repo_add_window.get_values()
|
||||
|
||||
if index == 1:
|
||||
cmd = ["borg", "init", "--log-json", f"--encryption={params['encryption']}", params['repo_url']]
|
||||
else:
|
||||
cmd = ["borg", "list", "--json", params['repo_url']]
|
||||
|
||||
self.set_status('Connecting to repo...', 0)
|
||||
thread = BorgThread(self, cmd, params)
|
||||
thread.updated.connect(self.repo_add_update_log)
|
||||
thread.result.connect(self.repo_add_result)
|
||||
thread.start()
|
||||
else:
|
||||
self.profile.repo = self.repoSelector.currentData()
|
||||
self.profile.save()
|
||||
self.init_repo_stats()
|
||||
|
||||
def repo_add_update_log(self, text):
|
||||
self.set_status(text)
|
||||
|
||||
def repo_add_result(self, result):
|
||||
if result['returncode'] == 0:
|
||||
self.set_status('Successfully connected to repo.', 100)
|
||||
new_repo, _ = RepoModel.get_or_create(
|
||||
url=result['params']['repo_url'],
|
||||
defaults={
|
||||
'password': result['params']['password'],
|
||||
# 'encryption': result['params'].get('encryption', '')
|
||||
}
|
||||
)
|
||||
if 'cache' in result['data']:
|
||||
stats = result['data']['cache']['stats']
|
||||
new_repo.total_size = stats['total_size']
|
||||
new_repo.unique_csize = stats['unique_csize']
|
||||
new_repo.unique_size = stats['unique_size']
|
||||
new_repo.total_unique_chunks = stats['total_unique_chunks']
|
||||
new_repo.encryption = result['data']['encryption']['mode']
|
||||
new_repo.save()
|
||||
self.profile.repo = new_repo.id
|
||||
self.profile.save()
|
||||
|
||||
if 'archives' in result['data'].keys():
|
||||
for snapshot in result['data']['archives']:
|
||||
new_snapshot, _ = SnapshotModel.get_or_create(
|
||||
snapshot_id=snapshot['id'],
|
||||
defaults={
|
||||
'repo': new_repo.id,
|
||||
'name': snapshot['name'],
|
||||
'time': parser.parse(snapshot['time'])
|
||||
}
|
||||
)
|
||||
new_snapshot.save()
|
||||
self.repoSelector.addItem(new_repo.url, new_repo.id)
|
||||
self.repoSelector.setCurrentIndex(self.repoSelector.count()-1)
|
||||
self.init_snapshots()
|
|
@ -0,0 +1,75 @@
|
|||
import os
|
||||
from PyQt5 import uic, QtCore
|
||||
from PyQt5.QtWidgets import QFileDialog, QTableWidgetItem, QTableView, QHeaderView
|
||||
|
||||
from .borg_runner import BorgThread
|
||||
|
||||
uifile = os.path.join(os.path.dirname(__file__), 'UI/snapshottab.ui')
|
||||
SnapshotUI, SnapshotBase = uic.loadUiType(uifile)
|
||||
|
||||
|
||||
class SnapshotTab(SnapshotBase, SnapshotUI):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(parent)
|
||||
self.profile = self.window().profile
|
||||
|
||||
header = self.snapshotTable.horizontalHeader()
|
||||
header.setVisible(True)
|
||||
header.setSectionResizeMode(0, QHeaderView.Stretch)
|
||||
header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
||||
|
||||
self.snapshotTable.setSelectionBehavior(QTableView.SelectRows)
|
||||
self.snapshotTable.setEditTriggers(QTableView.NoEditTriggers)
|
||||
|
||||
self.snapshotMountButton.clicked.connect(self.snapshot_mount)
|
||||
self.snapshotDeleteButton.clicked.connect(self.snapshot_mount)
|
||||
self.snapshotRefreshButton.clicked.connect(self.snapshot_mount)
|
||||
|
||||
self.populate()
|
||||
|
||||
def populate(self):
|
||||
if self.profile.repo:
|
||||
snapshots = [s for s in self.profile.repo.snapshots.select()]
|
||||
|
||||
for row, snapshot in enumerate(snapshots):
|
||||
self.snapshotTable.insertRow(row)
|
||||
self.snapshotTable.setItem(row, 0, QTableWidgetItem(snapshot.name))
|
||||
formatted_time = snapshot.time.strftime('%Y-%m-%d %H:%M')
|
||||
self.snapshotTable.setItem(row, 1, QTableWidgetItem(formatted_time))
|
||||
self.snapshotTable.setRowCount(len(snapshots))
|
||||
|
||||
def snapshot_mount(self):
|
||||
cmd = ['borg', 'mount', '--log-json']
|
||||
row_selected = self.snapshotTable.selectionModel().selectedRows()
|
||||
if row_selected:
|
||||
snapshot_cell = self.snapshotTable.item(row_selected[0].row(), 0)
|
||||
if snapshot_cell:
|
||||
snapshot_name = snapshot_cell.text()
|
||||
cmd.append(f'{self.profile.repo.url}::{snapshot_name}')
|
||||
else:
|
||||
cmd.append(f'{self.profile.repo.url}')
|
||||
else:
|
||||
cmd.append(f'{self.profile.repo.url}')
|
||||
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.ShowDirsOnly
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
mountPoint = QFileDialog.getExistingDirectory(
|
||||
self, "Choose Mount Point", "", options=options)
|
||||
if mountPoint:
|
||||
cmd.append(mountPoint)
|
||||
|
||||
self.set_status('Mounting snapshot into folder', 0)
|
||||
thread = BorgThread(self, cmd, {})
|
||||
thread.updated.connect(self.mount_update_log)
|
||||
thread.result.connect(self.mount_get_result)
|
||||
thread.start()
|
||||
|
||||
def mount_update_log(self, text):
|
||||
self.mountErrors.setText(text)
|
||||
|
||||
def mount_get_result(self, result):
|
||||
self.set_status(progress_max=100)
|
||||
if result['returncode'] == 0:
|
||||
self.set_status('Mounted successfully.')
|
|
@ -0,0 +1,36 @@
|
|||
import os
|
||||
from PyQt5 import uic, QtCore
|
||||
from PyQt5.QtWidgets import QFileDialog
|
||||
from .models import SourceDirModel
|
||||
|
||||
uifile = os.path.join(os.path.dirname(__file__), 'UI/sourcetab.ui')
|
||||
SourceUI, SourceBase = uic.loadUiType(uifile)
|
||||
|
||||
|
||||
class SourceTab(SourceBase, SourceUI):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(parent)
|
||||
self.profile = self.window().profile
|
||||
|
||||
self.sourceAdd.clicked.connect(self.source_add)
|
||||
self.sourceRemove.clicked.connect(self.source_remove)
|
||||
for source in SourceDirModel.select():
|
||||
self.sourceDirectoriesWidget.addItem(source.dir)
|
||||
|
||||
def source_add(self):
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.ShowDirsOnly
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
fileName = QFileDialog.getExistingDirectory(
|
||||
self, "Choose Backup Directory", "", options=options)
|
||||
if fileName:
|
||||
self.sourceDirectoriesWidget.addItem(fileName)
|
||||
new_source = SourceDirModel(dir=fileName)
|
||||
new_source.save()
|
||||
|
||||
def source_remove(self):
|
||||
item = self.sourceDirectoriesWidget.takeItem(self.sourceDirectoriesWidget.currentRow())
|
||||
db_item = SourceDirModel.get(dir=item.text())
|
||||
db_item.delete_instance()
|
||||
item = None
|
|
@ -1,28 +0,0 @@
|
|||
import os
|
||||
from paramiko.rsakey import RSAKey
|
||||
from paramiko.ecdsakey import ECDSAKey
|
||||
from paramiko.ed25519key import Ed25519Key
|
||||
from paramiko import SSHException
|
||||
|
||||
|
||||
def get_private_keys():
|
||||
key_formats = [RSAKey, ECDSAKey, Ed25519Key]
|
||||
|
||||
ssh_folder = os.path.expanduser('~/.ssh')
|
||||
|
||||
available_private_keys = []
|
||||
for key in os.listdir(ssh_folder):
|
||||
for key_format in key_formats:
|
||||
try:
|
||||
parsed_key = key_format.from_private_key_file(os.path.join(ssh_folder, key))
|
||||
key_details = {
|
||||
'filename': key,
|
||||
'format': parsed_key.get_name(),
|
||||
'bits': parsed_key.get_bits(),
|
||||
'fingerprint': parsed_key.get_fingerprint().hex()
|
||||
}
|
||||
available_private_keys.append(key_details)
|
||||
except (SSHException, UnicodeDecodeError, IsADirectoryError):
|
||||
continue
|
||||
|
||||
return available_private_keys
|
|
@ -1,5 +1,36 @@
|
|||
import os
|
||||
from paramiko.rsakey import RSAKey
|
||||
from paramiko.ecdsakey import ECDSAKey
|
||||
from paramiko.ed25519key import Ed25519Key
|
||||
from paramiko import SSHException
|
||||
|
||||
def prettyByes(size):
|
||||
|
||||
def get_private_keys():
|
||||
"""Find SSH keys in standard folder."""
|
||||
key_formats = [RSAKey, ECDSAKey, Ed25519Key]
|
||||
|
||||
ssh_folder = os.path.expanduser('~/.ssh')
|
||||
|
||||
available_private_keys = []
|
||||
if os.path.isdir(ssh_folder):
|
||||
for key in os.listdir(ssh_folder):
|
||||
for key_format in key_formats:
|
||||
try:
|
||||
parsed_key = key_format.from_private_key_file(os.path.join(ssh_folder, key))
|
||||
key_details = {
|
||||
'filename': key,
|
||||
'format': parsed_key.get_name(),
|
||||
'bits': parsed_key.get_bits(),
|
||||
'fingerprint': parsed_key.get_fingerprint().hex()
|
||||
}
|
||||
available_private_keys.append(key_details)
|
||||
except (SSHException, UnicodeDecodeError, IsADirectoryError):
|
||||
continue
|
||||
|
||||
return available_private_keys
|
||||
|
||||
|
||||
def prettyBytes(size):
|
||||
"""from https://stackoverflow.com/questions/12523586/
|
||||
python-format-size-application-converting-b-to-kb-mb-gb-tb/37423778"""
|
||||
if type(size) != int:
|
||||
|
|
Loading…
Reference in New Issue