Make BorgThread more independent. Fix issue when calling from apscheduler.

This commit is contained in:
Manu 2018-10-30 01:25:28 +08:00
parent b9d4d57f33
commit 4f0de320c4
12 changed files with 385 additions and 55 deletions

View File

@ -1,14 +1,21 @@
import sys
import os
from PyQt5.QtWidgets import QApplication
# Ensures resource file in icons-folder is found
from vorta.utils import get_asset
sys.path.append(os.path.dirname(get_asset('icons/collection.rc')))
from vorta.tray_menu import TrayMenu
from vorta.scheduler import init_scheduler
from vorta.models import BackupProfileModel
app = QApplication(sys.argv)
app.thread = None
app.setQuitOnLastWindowClosed(False)
TrayMenu(app)
app.scheduler = init_scheduler()
TrayMenu(app)
app.profile = BackupProfileModel.get(id=1)
if not getattr(sys, 'frozen', False):

View File

@ -197,6 +197,8 @@
</property>
</action>
</widget>
<resources/>
<resources>
<include location="../icons/collection.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -29,7 +29,7 @@ font-weight: bold;
}</string>
</property>
<property name="currentIndex">
<number>2</number>
<number>1</number>
</property>
<widget class="QWidget" name="schedule">
<property name="geometry">
@ -47,8 +47,8 @@ font-weight: bold;
</font>
</property>
<attribute name="icon">
<iconset>
<normaloff>../icons/clock-o.svg</normaloff>../icons/clock-o.svg</iconset>
<iconset resource="../icons/collection.qrc">
<normaloff>:/icons/clock-o.svg</normaloff>:/icons/clock-o.svg</iconset>
</attribute>
<attribute name="label">
<string>Schedule</string>
@ -256,8 +256,8 @@ font-weight: bold;
</rect>
</property>
<attribute name="icon">
<iconset>
<normaloff>../icons/wifi.svg</normaloff>../icons/wifi.svg</iconset>
<iconset resource="../icons/collection.qrc">
<normaloff>:/icons/wifi.svg</normaloff>:/icons/wifi.svg</iconset>
</attribute>
<attribute name="label">
<string>Networks</string>
@ -276,9 +276,17 @@ font-weight: bold;
</layout>
</widget>
<widget class="QWidget" name="page">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>663</width>
<height>381</height>
</rect>
</property>
<attribute name="icon">
<iconset>
<normaloff>../icons/tasks.svg</normaloff>../icons/tasks.svg</iconset>
<iconset resource="../icons/collection.qrc">
<normaloff>:/icons/tasks.svg</normaloff>:/icons/tasks.svg</iconset>
</attribute>
<attribute name="label">
<string>Log</string>
@ -333,6 +341,8 @@ font-weight: bold;
</item>
</layout>
</widget>
<resources/>
<resources>
<include location="../icons/collection.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,266 @@
# -*- coding: utf-8 -*-
# Resource object code
#
# Created by: The Resource Compiler for PyQt5 (Qt v5.11.2)
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore
qt_resource_data = b"\
\x00\x00\x03\x19\
\x3c\
\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\
\x2d\x38\x22\x3f\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\x64\x74\x68\
\x3d\x22\x32\x30\x34\x38\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\
\x31\x37\x39\x32\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\
\x20\x30\x20\x32\x30\x34\x38\x20\x31\x37\x39\x32\x22\x20\x78\x6d\
\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\
\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x30\x32\x34\x20\
\x31\x35\x32\x33\x71\x2d\x32\x30\x20\x30\x2d\x39\x33\x2d\x37\x33\
\x2e\x35\x74\x2d\x37\x33\x2d\x39\x33\x2e\x35\x71\x30\x2d\x33\x32\
\x20\x36\x32\x2e\x35\x2d\x35\x34\x74\x31\x30\x33\x2e\x35\x2d\x32\
\x32\x20\x31\x30\x33\x2e\x35\x20\x32\x32\x20\x36\x32\x2e\x35\x20\
\x35\x34\x71\x30\x20\x32\x30\x2d\x37\x33\x20\x39\x33\x2e\x35\x74\
\x2d\x39\x33\x20\x37\x33\x2e\x35\x7a\x6d\x32\x37\x30\x2d\x32\x37\
\x31\x71\x2d\x32\x20\x30\x2d\x34\x30\x2d\x32\x35\x74\x2d\x31\x30\
\x31\x2e\x35\x2d\x35\x30\x2d\x31\x32\x38\x2e\x35\x2d\x32\x35\x2d\
\x31\x32\x38\x2e\x35\x20\x32\x35\x2d\x31\x30\x31\x20\x35\x30\x2d\
\x34\x30\x2e\x35\x20\x32\x35\x71\x2d\x31\x38\x20\x30\x2d\x39\x33\
\x2e\x35\x2d\x37\x35\x74\x2d\x37\x35\x2e\x35\x2d\x39\x33\x71\x30\
\x2d\x31\x33\x20\x31\x30\x2d\x32\x33\x20\x37\x38\x2d\x37\x37\x20\
\x31\x39\x36\x2d\x31\x32\x31\x74\x32\x33\x33\x2d\x34\x34\x20\x32\
\x33\x33\x20\x34\x34\x20\x31\x39\x36\x20\x31\x32\x31\x71\x31\x30\
\x20\x31\x30\x20\x31\x30\x20\x32\x33\x20\x30\x20\x31\x38\x2d\x37\
\x35\x2e\x35\x20\x39\x33\x74\x2d\x39\x33\x2e\x35\x20\x37\x35\x7a\
\x6d\x32\x37\x33\x2d\x32\x37\x32\x71\x2d\x31\x31\x20\x30\x2d\x32\
\x33\x2d\x38\x2d\x31\x33\x36\x2d\x31\x30\x35\x2d\x32\x35\x32\x2d\
\x31\x35\x34\x2e\x35\x74\x2d\x32\x36\x38\x2d\x34\x39\x2e\x35\x71\
\x2d\x38\x35\x20\x30\x2d\x31\x37\x30\x2e\x35\x20\x32\x32\x74\x2d\
\x31\x34\x39\x20\x35\x33\x2d\x31\x31\x33\x2e\x35\x20\x36\x32\x2d\
\x37\x39\x20\x35\x33\x2d\x33\x31\x20\x32\x32\x71\x2d\x31\x37\x20\
\x30\x2d\x39\x32\x2d\x37\x35\x74\x2d\x37\x35\x2d\x39\x33\x71\x30\
\x2d\x31\x32\x20\x31\x30\x2d\x32\x32\x20\x31\x33\x32\x2d\x31\x33\
\x32\x20\x33\x32\x30\x2d\x32\x30\x35\x74\x33\x38\x30\x2d\x37\x33\
\x20\x33\x38\x30\x20\x37\x33\x20\x33\x32\x30\x20\x32\x30\x35\x71\
\x31\x30\x20\x31\x30\x20\x31\x30\x20\x32\x32\x20\x30\x20\x31\x38\
\x2d\x37\x35\x20\x39\x33\x74\x2d\x39\x32\x20\x37\x35\x7a\x6d\x32\
\x37\x31\x2d\x32\x37\x31\x71\x2d\x31\x31\x20\x30\x2d\x32\x32\x2d\
\x39\x2d\x31\x37\x39\x2d\x31\x35\x37\x2d\x33\x37\x31\x2e\x35\x2d\
\x32\x33\x36\x2e\x35\x74\x2d\x34\x32\x30\x2e\x35\x2d\x37\x39\x2e\
\x35\x2d\x34\x32\x30\x2e\x35\x20\x37\x39\x2e\x35\x2d\x33\x37\x31\
\x2e\x35\x20\x32\x33\x36\x2e\x35\x71\x2d\x31\x31\x20\x39\x2d\x32\
\x32\x20\x39\x2d\x31\x37\x20\x30\x2d\x39\x32\x2e\x35\x2d\x37\x35\
\x74\x2d\x37\x35\x2e\x35\x2d\x39\x33\x71\x30\x2d\x31\x33\x20\x31\
\x30\x2d\x32\x33\x20\x31\x38\x37\x2d\x31\x38\x36\x20\x34\x34\x35\
\x2d\x32\x38\x38\x74\x35\x32\x37\x2d\x31\x30\x32\x20\x35\x32\x37\
\x20\x31\x30\x32\x20\x34\x34\x35\x20\x32\x38\x38\x71\x31\x30\x20\
\x31\x30\x20\x31\x30\x20\x32\x33\x20\x30\x20\x31\x38\x2d\x37\x35\
\x2e\x35\x20\x39\x33\x74\x2d\x39\x32\x2e\x35\x20\x37\x35\x7a\x22\
\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\
\x00\x00\x01\x89\
\x3c\
\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\
\x2d\x38\x22\x3f\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\x64\x74\x68\
\x3d\x22\x32\x30\x34\x38\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\
\x31\x37\x39\x32\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\
\x20\x30\x20\x32\x30\x34\x38\x20\x31\x37\x39\x32\x22\x20\x78\x6d\
\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\
\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x32\x35\x36\x20\x31\
\x35\x33\x36\x68\x37\x36\x38\x76\x2d\x35\x31\x32\x68\x2d\x37\x36\
\x38\x76\x35\x31\x32\x7a\x6d\x31\x30\x32\x34\x2d\x35\x31\x32\x68\
\x35\x31\x32\x76\x2d\x37\x36\x38\x68\x2d\x37\x36\x38\x76\x32\x35\
\x36\x68\x39\x36\x71\x36\x36\x20\x30\x20\x31\x31\x33\x20\x34\x37\
\x74\x34\x37\x20\x31\x31\x33\x76\x33\x35\x32\x7a\x6d\x37\x36\x38\
\x2d\x38\x36\x34\x76\x39\x36\x30\x71\x30\x20\x36\x36\x2d\x34\x37\
\x20\x31\x31\x33\x74\x2d\x31\x31\x33\x20\x34\x37\x68\x2d\x36\x30\
\x38\x76\x33\x35\x32\x71\x30\x20\x36\x36\x2d\x34\x37\x20\x31\x31\
\x33\x74\x2d\x31\x31\x33\x20\x34\x37\x68\x2d\x39\x36\x30\x71\x2d\
\x36\x36\x20\x30\x2d\x31\x31\x33\x2d\x34\x37\x74\x2d\x34\x37\x2d\
\x31\x31\x33\x76\x2d\x39\x36\x30\x71\x30\x2d\x36\x36\x20\x34\x37\
\x2d\x31\x31\x33\x74\x31\x31\x33\x2d\x34\x37\x68\x36\x30\x38\x76\
\x2d\x33\x35\x32\x71\x30\x2d\x36\x36\x20\x34\x37\x2d\x31\x31\x33\
\x74\x31\x31\x33\x2d\x34\x37\x68\x39\x36\x30\x71\x36\x36\x20\x30\
\x20\x31\x31\x33\x20\x34\x37\x74\x34\x37\x20\x31\x31\x33\x7a\x22\
\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\
\x00\x00\x02\x12\
\x3c\
\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\
\x2d\x38\x22\x3f\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\x64\x74\x68\
\x3d\x22\x31\x37\x39\x32\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\
\x31\x37\x39\x32\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\
\x20\x30\x20\x31\x37\x39\x32\x20\x31\x37\x39\x32\x22\x20\x78\x6d\
\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\
\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x30\x32\x34\x20\
\x31\x34\x30\x38\x68\x36\x34\x30\x76\x2d\x31\x32\x38\x68\x2d\x36\
\x34\x30\x76\x31\x32\x38\x7a\x6d\x2d\x33\x38\x34\x2d\x35\x31\x32\
\x68\x31\x30\x32\x34\x76\x2d\x31\x32\x38\x68\x2d\x31\x30\x32\x34\
\x76\x31\x32\x38\x7a\x6d\x36\x34\x30\x2d\x35\x31\x32\x68\x33\x38\
\x34\x76\x2d\x31\x32\x38\x68\x2d\x33\x38\x34\x76\x31\x32\x38\x7a\
\x6d\x35\x31\x32\x20\x38\x33\x32\x76\x32\x35\x36\x71\x30\x20\x32\
\x36\x2d\x31\x39\x20\x34\x35\x74\x2d\x34\x35\x20\x31\x39\x68\x2d\
\x31\x36\x36\x34\x71\x2d\x32\x36\x20\x30\x2d\x34\x35\x2d\x31\x39\
\x74\x2d\x31\x39\x2d\x34\x35\x76\x2d\x32\x35\x36\x71\x30\x2d\x32\
\x36\x20\x31\x39\x2d\x34\x35\x74\x34\x35\x2d\x31\x39\x68\x31\x36\
\x36\x34\x71\x32\x36\x20\x30\x20\x34\x35\x20\x31\x39\x74\x31\x39\
\x20\x34\x35\x7a\x6d\x30\x2d\x35\x31\x32\x76\x32\x35\x36\x71\x30\
\x20\x32\x36\x2d\x31\x39\x20\x34\x35\x74\x2d\x34\x35\x20\x31\x39\
\x68\x2d\x31\x36\x36\x34\x71\x2d\x32\x36\x20\x30\x2d\x34\x35\x2d\
\x31\x39\x74\x2d\x31\x39\x2d\x34\x35\x76\x2d\x32\x35\x36\x71\x30\
\x2d\x32\x36\x20\x31\x39\x2d\x34\x35\x74\x34\x35\x2d\x31\x39\x68\
\x31\x36\x36\x34\x71\x32\x36\x20\x30\x20\x34\x35\x20\x31\x39\x74\
\x31\x39\x20\x34\x35\x7a\x6d\x30\x2d\x35\x31\x32\x76\x32\x35\x36\
\x71\x30\x20\x32\x36\x2d\x31\x39\x20\x34\x35\x74\x2d\x34\x35\x20\
\x31\x39\x68\x2d\x31\x36\x36\x34\x71\x2d\x32\x36\x20\x30\x2d\x34\
\x35\x2d\x31\x39\x74\x2d\x31\x39\x2d\x34\x35\x76\x2d\x32\x35\x36\
\x71\x30\x2d\x32\x36\x20\x31\x39\x2d\x34\x35\x74\x34\x35\x2d\x31\
\x39\x68\x31\x36\x36\x34\x71\x32\x36\x20\x30\x20\x34\x35\x20\x31\
\x39\x74\x31\x39\x20\x34\x35\x7a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\
\x3e\
\x00\x00\x01\xfb\
\x3c\
\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\
\x2d\x38\x22\x3f\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\x64\x74\x68\
\x3d\x22\x31\x37\x39\x32\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\
\x31\x37\x39\x32\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\
\x20\x30\x20\x31\x37\x39\x32\x20\x31\x37\x39\x32\x22\x20\x78\x6d\
\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\
\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x30\x32\x34\x20\
\x35\x34\x34\x76\x34\x34\x38\x71\x30\x20\x31\x34\x2d\x39\x20\x32\
\x33\x74\x2d\x32\x33\x20\x39\x68\x2d\x33\x32\x30\x71\x2d\x31\x34\
\x20\x30\x2d\x32\x33\x2d\x39\x74\x2d\x39\x2d\x32\x33\x76\x2d\x36\
\x34\x71\x30\x2d\x31\x34\x20\x39\x2d\x32\x33\x74\x32\x33\x2d\x39\
\x68\x32\x32\x34\x76\x2d\x33\x35\x32\x71\x30\x2d\x31\x34\x20\x39\
\x2d\x32\x33\x74\x32\x33\x2d\x39\x68\x36\x34\x71\x31\x34\x20\x30\
\x20\x32\x33\x20\x39\x74\x39\x20\x32\x33\x7a\x6d\x34\x31\x36\x20\
\x33\x35\x32\x71\x30\x2d\x31\x34\x38\x2d\x37\x33\x2d\x32\x37\x33\
\x74\x2d\x31\x39\x38\x2d\x31\x39\x38\x2d\x32\x37\x33\x2d\x37\x33\
\x2d\x32\x37\x33\x20\x37\x33\x2d\x31\x39\x38\x20\x31\x39\x38\x2d\
\x37\x33\x20\x32\x37\x33\x20\x37\x33\x20\x32\x37\x33\x20\x31\x39\
\x38\x20\x31\x39\x38\x20\x32\x37\x33\x20\x37\x33\x20\x32\x37\x33\
\x2d\x37\x33\x20\x31\x39\x38\x2d\x31\x39\x38\x20\x37\x33\x2d\x32\
\x37\x33\x7a\x6d\x32\x32\x34\x20\x30\x71\x30\x20\x32\x30\x39\x2d\
\x31\x30\x33\x20\x33\x38\x35\x2e\x35\x74\x2d\x32\x37\x39\x2e\x35\
\x20\x32\x37\x39\x2e\x35\x2d\x33\x38\x35\x2e\x35\x20\x31\x30\x33\
\x2d\x33\x38\x35\x2e\x35\x2d\x31\x30\x33\x2d\x32\x37\x39\x2e\x35\
\x2d\x32\x37\x39\x2e\x35\x2d\x31\x30\x33\x2d\x33\x38\x35\x2e\x35\
\x20\x31\x30\x33\x2d\x33\x38\x35\x2e\x35\x20\x32\x37\x39\x2e\x35\
\x2d\x32\x37\x39\x2e\x35\x20\x33\x38\x35\x2e\x35\x2d\x31\x30\x33\
\x20\x33\x38\x35\x2e\x35\x20\x31\x30\x33\x20\x32\x37\x39\x2e\x35\
\x20\x32\x37\x39\x2e\x35\x20\x31\x30\x33\x20\x33\x38\x35\x2e\x35\
\x7a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\
\x00\x00\x01\xfb\
\x3c\
\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\
\x2d\x38\x22\x3f\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\x64\x74\x68\
\x3d\x22\x31\x37\x39\x32\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\
\x31\x37\x39\x32\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\
\x20\x30\x20\x31\x37\x39\x32\x20\x31\x37\x39\x32\x22\x20\x78\x6d\
\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\
\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x32\x38\x20\x31\
\x34\x30\x38\x68\x31\x30\x32\x34\x76\x2d\x31\x32\x38\x68\x2d\x31\
\x30\x32\x34\x76\x31\x32\x38\x7a\x6d\x30\x2d\x35\x31\x32\x68\x31\
\x30\x32\x34\x76\x2d\x31\x32\x38\x68\x2d\x31\x30\x32\x34\x76\x31\
\x32\x38\x7a\x6d\x31\x35\x36\x38\x20\x34\x34\x38\x71\x30\x2d\x34\
\x30\x2d\x32\x38\x2d\x36\x38\x74\x2d\x36\x38\x2d\x32\x38\x2d\x36\
\x38\x20\x32\x38\x2d\x32\x38\x20\x36\x38\x20\x32\x38\x20\x36\x38\
\x20\x36\x38\x20\x32\x38\x20\x36\x38\x2d\x32\x38\x20\x32\x38\x2d\
\x36\x38\x7a\x6d\x2d\x31\x35\x36\x38\x2d\x39\x36\x30\x68\x31\x30\
\x32\x34\x76\x2d\x31\x32\x38\x68\x2d\x31\x30\x32\x34\x76\x31\x32\
\x38\x7a\x6d\x31\x35\x36\x38\x20\x34\x34\x38\x71\x30\x2d\x34\x30\
\x2d\x32\x38\x2d\x36\x38\x74\x2d\x36\x38\x2d\x32\x38\x2d\x36\x38\
\x20\x32\x38\x2d\x32\x38\x20\x36\x38\x20\x32\x38\x20\x36\x38\x20\
\x36\x38\x20\x32\x38\x20\x36\x38\x2d\x32\x38\x20\x32\x38\x2d\x36\
\x38\x7a\x6d\x30\x2d\x35\x31\x32\x71\x30\x2d\x34\x30\x2d\x32\x38\
\x2d\x36\x38\x74\x2d\x36\x38\x2d\x32\x38\x2d\x36\x38\x20\x32\x38\
\x2d\x32\x38\x20\x36\x38\x20\x32\x38\x20\x36\x38\x20\x36\x38\x20\
\x32\x38\x20\x36\x38\x2d\x32\x38\x20\x32\x38\x2d\x36\x38\x7a\x6d\
\x39\x36\x20\x38\x33\x32\x76\x33\x38\x34\x68\x2d\x31\x37\x39\x32\
\x76\x2d\x33\x38\x34\x68\x31\x37\x39\x32\x7a\x6d\x30\x2d\x35\x31\
\x32\x76\x33\x38\x34\x68\x2d\x31\x37\x39\x32\x76\x2d\x33\x38\x34\
\x68\x31\x37\x39\x32\x7a\x6d\x30\x2d\x35\x31\x32\x76\x33\x38\x34\
\x68\x2d\x31\x37\x39\x32\x76\x2d\x33\x38\x34\x68\x31\x37\x39\x32\
\x7a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\
"
qt_resource_name = b"\
\x00\x05\
\x00\x6f\xa6\x53\
\x00\x69\
\x00\x63\x00\x6f\x00\x6e\x00\x73\
\x00\x08\
\x0f\xcc\x55\x67\
\x00\x77\
\x00\x69\x00\x66\x00\x69\x00\x2e\x00\x73\x00\x76\x00\x67\
\x00\x12\
\x05\x98\xe2\x07\
\x00\x77\
\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x2d\x00\x72\x00\x65\x00\x73\x00\x74\x00\x6f\x00\x72\x00\x65\x00\x2e\x00\x73\x00\x76\
\x00\x67\
\x00\x09\
\x0a\x26\xaf\xc7\
\x00\x74\
\x00\x61\x00\x73\x00\x6b\x00\x73\x00\x2e\x00\x73\x00\x76\x00\x67\
\x00\x0b\
\x0f\x16\x31\xe7\
\x00\x63\
\x00\x6c\x00\x6f\x00\x63\x00\x6b\x00\x2d\x00\x6f\x00\x2e\x00\x73\x00\x76\x00\x67\
\x00\x0a\
\x0c\xca\x63\xe7\
\x00\x73\
\x00\x65\x00\x72\x00\x76\x00\x65\x00\x72\x00\x2e\x00\x73\x00\x76\x00\x67\
"
qt_resource_struct_v1 = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x02\
\x00\x00\x00\x26\x00\x00\x00\x00\x00\x01\x00\x00\x03\x1d\
\x00\x00\x00\x50\x00\x00\x00\x00\x00\x01\x00\x00\x04\xaa\
\x00\x00\x00\x84\x00\x00\x00\x00\x00\x01\x00\x00\x08\xbf\
\x00\x00\x00\x68\x00\x00\x00\x00\x00\x01\x00\x00\x06\xc0\
\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
"
qt_resource_struct_v2 = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x26\x00\x00\x00\x00\x00\x01\x00\x00\x03\x1d\
\x00\x00\x01\x66\xbf\xa1\xb1\x2c\
\x00\x00\x00\x50\x00\x00\x00\x00\x00\x01\x00\x00\x04\xaa\
\x00\x00\x01\x66\xbf\x9f\xde\x3e\
\x00\x00\x00\x84\x00\x00\x00\x00\x00\x01\x00\x00\x08\xbf\
\x00\x00\x01\x66\xbf\xa1\x0f\xda\
\x00\x00\x00\x68\x00\x00\x00\x00\x00\x01\x00\x00\x06\xc0\
\x00\x00\x01\x66\xbf\x9d\x97\x00\
\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x66\xbf\x9f\x19\x44\
"
qt_version = [int(v) for v in QtCore.qVersion().split('.')]
if qt_version < [5, 8, 0]:
rcc_version = 1
qt_resource_struct = qt_resource_struct_v1
else:
rcc_version = 2
qt_resource_struct = qt_resource_struct_v2
def qInitResources():
QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
def qCleanupResources():
QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
qInitResources()

View File

@ -10,16 +10,16 @@ from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication
from subprocess import Popen, PIPE
from .models import SourceDirModel, BackupProfileModel, EventLogModel
from .models import SourceDirModel, BackupProfileModel, EventLogModel, WifiSettingModel
from .utils import get_current_wifi
class BorgThread(QtCore.QThread):
updated = QtCore.pyqtSignal(str)
result = QtCore.pyqtSignal(object)
def __init__(self, parent, cmd, params):
super().__init__(parent)
def __init__(self, cmd, params):
super().__init__()
# Find packaged borg binary. Prefer globally installed.
if not shutil.which('borg'):
meipass_borg = os.path.join(sys._MEIPASS, 'bin', 'borg')
@ -73,32 +73,43 @@ class BorgThread(QtCore.QThread):
self.result.emit(result)
@classmethod
def create_thread_factory(cls):
"""`borg create` is called from different places and need preparation.
Centralize it here and return a thread to the caller.
def prepare_runner(cls):
"""
`borg create` is called from different places and needs some preparation.
Centralize it here and return the required arguments to the caller.
"""
profile = BackupProfileModel.get(id=1)
app = QApplication.instance()
n_backup_folders = SourceDirModel.select().count()
ret = {
'ok': False,
}
params = {'password': keyring.get_password("vorta-repo", profile.repo.url)}
if app.thread and app.thread.isRunning():
ret['message'] = 'Backup is already in progress.'
return ret
if n_backup_folders == 0:
ret['message'] = 'Add some folders to back up first.'
return ret
if profile.repo is None:
ret['message'] = 'Add a remote backup repository first.'
return ret
n_backup_folders = SourceDirModel.select().count()
if n_backup_folders == 0:
ret['message'] = 'Add some folders to back up first.'
return ret
current_wifi = get_current_wifi()
if current_wifi is not None:
wifi_is_disallowed = WifiSettingModel.select().where(
WifiSettingModel.ssid == current_wifi &
WifiSettingModel.allowed == False &
WifiSettingModel.profile == profile.id
)
if wifi_is_disallowed.count() > 0:
ret['message'] = 'Current Wifi is not allowed.'
return ret
params = {'password': keyring.get_password("vorta-repo", profile.repo.url)}
cmd = ['borg', 'create', '--list', '--info', '--log-json', '--json', '-C', profile.compression]
# Add excludes
@ -128,10 +139,10 @@ class BorgThread(QtCore.QThread):
for f in SourceDirModel.select():
cmd.append(f.dir)
app.thread = cls(app, cmd, params)
ret['message'] = 'Starting Backup.'
ret['message'] = 'Ready to start backup..'
ret['ok'] = True
ret['thread'] = app.thread
ret['cmd'] = cmd
ret['params'] = params
return ret

View File

@ -38,7 +38,7 @@ class RepoModel(peewee.Model):
class BackupProfileModel(peewee.Model):
"""Allows the user to switch between different configurations."""
name = peewee.CharField()
added_at = peewee.DateTimeField(default=datetime.utcnow)
added_at = peewee.DateTimeField(default=datetime.now)
repo = peewee.ForeignKeyField(RepoModel, default=None, null=True)
ssh_key = peewee.CharField(default=None, null=True)
compression = peewee.CharField(default='lz4')
@ -91,7 +91,7 @@ class WifiSettingModel(peewee.Model):
class EventLogModel(peewee.Model):
"""Keep a log of background jobs."""
start_time = peewee.DateTimeField(default=datetime.utcnow)
start_time = peewee.DateTimeField(default=datetime.now)
category = peewee.CharField()
subcommand = peewee.CharField(null=True)
message = peewee.CharField(null=True)

View File

@ -1,21 +1,29 @@
from apscheduler.schedulers.qt import QtScheduler
from apscheduler.triggers import cron
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore
from .models import BackupProfileModel
from .models import BackupProfileModel, EventLogModel
from .borg_runner import BorgThread
def tick():
print('scheduler running')
def create_backup_task():
msg = BorgThread.prepare_runner()
if msg['ok']:
t = BorgThread(None, msg['cmd'], msg['params'])
t.start()
t.wait()
else:
error_log = EventLogModel(category='borg-factory', message=msg['message'])
error_log.save()
def init_scheduler():
s = QtScheduler()
app = QApplication.instance()
if hasattr(app, 'scheduler') and app.scheduler is not None:
app.scheduler.shutdown()
s = QtScheduler()
profile = BackupProfileModel.get(id=1)
if profile.schedule_mode == 'off':
return None
@ -26,6 +34,6 @@ def init_scheduler():
trigger = cron.CronTrigger(hour=profile.schedule_fixed_hour,
minute=profile.schedule_fixed_minute)
s.add_job(tick, trigger, id='create-backup')
s.add_job(create_backup_task, trigger, id='create-backup', misfire_grace_time=180)
s.start()
return s

View File

@ -1,5 +1,5 @@
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMenu, QApplication, QSystemTrayIcon
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QMenu, QApplication, QSystemTrayIcon, QMessageBox, QDialog
from .views.main_window import MainWindow
from PyQt5.QtGui import QIcon
@ -14,7 +14,7 @@ class TrayMenu(QSystemTrayIcon):
self.app = parent
menu = QMenu()
self.status = menu.addAction("Sleeping")
self.status = menu.addAction(self._get_scheduler_status())
self.status.setEnabled(False)
self.create_action = menu.addAction("Backup Now")
@ -49,25 +49,30 @@ class TrayMenu(QSystemTrayIcon):
QApplication.instance().quit()
def on_create_backup(self):
thread_msg = BorgThread.create_thread_factory()
if thread_msg['ok']:
thread_msg['thread'].start()
else:
error_dialog = QtWidgets.QErrorMessage()
error_dialog.showMessage(thread_msg['message'])
error_dialog.show()
def on_cancel_backup(self):
if self.app.thread and self.app.thread.isRunning():
self.app.thread.process.kill()
self.app.thread.terminate()
else:
msg = BorgThread.prepare_runner()
if msg['ok']:
self.app.thread = BorgThread(msg['cmd'], msg['params'])
self.app.thread.start()
else:
print(msg['message'])
# TODO: error dialog
def on_user_click(self):
"""Adjust labels to reflect current status."""
if self.app.thread and self.app.thread.isRunning():
self.status.setText('Backup in Progress')
self.create_action.setText('Cancel Backup')
self.create_action.triggered.connect(self.on_cancel_backup)
else:
self.status.setText('Sleeping')
self.status.setText(self._get_scheduler_status())
self.create_action.setText('Backup Now')
self.create_action.triggered.connect(self.on_create_backup)
def _get_scheduler_status(self):
if self.app.scheduler is not None:
job = self.app.scheduler.get_job('create-backup')
return f"Next run: {job.next_run_time.strftime('%Y-%m-%d %H:%M')}"
else:
return 'No backups scheduled'

View File

@ -6,6 +6,7 @@ from paramiko.ecdsakey import ECDSAKey
from paramiko.ed25519key import Ed25519Key
from paramiko import SSHException
from PyQt5.QtWidgets import QApplication
import subprocess
from .models import WifiSettingModel
@ -73,3 +74,20 @@ def get_sorted_wifis():
'allowed': True})
return WifiSettingModel.select().order_by(-WifiSettingModel.last_connected)
def get_current_wifi():
"""
Get current SSID or None if Wifi is off.
From https://gist.github.com/keithweaver/00edf356e8194b89ed8d3b7bbead000c
"""
cmd = ['/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport','-I']
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
out, err = process.communicate()
process.wait()
for line in out.decode("utf-8").split('\n'):
split_line = line.strip().split(':')
if split_line[0] == 'SSID':
return split_line[1]

View File

@ -56,15 +56,16 @@ class MainWindow(MainWindowBase, MainWindowUI):
self.createProgressText.repaint()
def create_action(self):
thread_msg = BorgThread.create_thread_factory()
if thread_msg['ok']:
self.set_status(thread_msg['message'], progress_max=0)
msg = BorgThread.prepare_runner()
if msg['ok']:
self.set_status(msg['message'], progress_max=0)
self.createStartBtn.setEnabled(False)
self.createStartBtn.repaint()
thread = thread_msg['thread']
thread = BorgThread(msg['cmd'], msg['params'])
thread.updated.connect(self.create_update_log)
thread.result.connect(self.create_get_result)
thread.start()
self.app.thread = thread
self.app.thread.start()
def create_update_log(self, text):
self.set_status(text)

View File

@ -39,6 +39,8 @@ class ScheduleTab(ScheduleBase, ScheduleUI):
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
if wifi.allowed:
item.setCheckState(QtCore.Qt.Checked)
else:
item.setCheckState(QtCore.Qt.Unchecked)
self.wifiListWidget.addItem(item)
def init_logs(self):