Migration from python threads and to QThread obj
Simple design and better performance, just start() thread and finished call back will show any error or msg, stop progress dialog and do cleanup. Full remove of QT process_events() calls.
This commit is contained in:
parent
35cdf5e1e5
commit
29ca4eb3a8
@ -23,9 +23,11 @@
|
||||
import traceback
|
||||
|
||||
import signal
|
||||
import quamash
|
||||
|
||||
from qubesadmin import Qubes, exc
|
||||
from qubesadmin import utils as admin_utils
|
||||
from qubesadmin import events
|
||||
from qubes.storage.file import get_disk_usage
|
||||
|
||||
from PyQt4 import QtCore # pylint: disable=import-error
|
||||
@ -35,18 +37,38 @@ from . import multiselectwidget
|
||||
|
||||
from . import backup_utils
|
||||
from . import utils
|
||||
|
||||
import grp
|
||||
import pwd
|
||||
import sys
|
||||
import os
|
||||
from . import thread_monitor
|
||||
import threading
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
import time
|
||||
|
||||
class BackupThread(QtCore.QThread):
|
||||
def __init__(self, vm):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.vm = vm
|
||||
self.msg = None
|
||||
|
||||
def run(self):
|
||||
msg = []
|
||||
try:
|
||||
if not self.vm.is_running():
|
||||
self.vm.start()
|
||||
self.vm.app.qubesd_call(
|
||||
'dom0', 'admin.backup.Execute',
|
||||
backup_utils.get_profile_name(True))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
msg.append(str(ex))
|
||||
|
||||
if msg:
|
||||
self.msg = '\n'.join(msg)
|
||||
|
||||
|
||||
class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
|
||||
|
||||
def __init__(self, qt_app, qubes_app, parent=None):
|
||||
def __init__(self, qt_app, qubes_app, dispatcher, parent=None):
|
||||
super(BackupVMsWindow, self).__init__(parent)
|
||||
|
||||
self.qt_app = qt_app
|
||||
@ -54,8 +76,6 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
|
||||
self.backup_settings = QtCore.QSettings()
|
||||
|
||||
self.selected_vms = []
|
||||
self.canceled = False
|
||||
self.thread_monitor = None
|
||||
|
||||
self.setupUi(self)
|
||||
|
||||
@ -112,6 +132,17 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
|
||||
selected = self.load_settings()
|
||||
self.__fill_vms_list__(selected)
|
||||
|
||||
# Connect backup events for progress_bar
|
||||
self.progress_bar.setMinimum(0)
|
||||
self.progress_bar.setMaximum(100)
|
||||
self.dispatcher = dispatcher
|
||||
dispatcher.add_handler('backup-progress', self.on_backup_progress)
|
||||
|
||||
def on_backup_progress(self, __submitter, _event, **kwargs):
|
||||
print(kwargs['progress'])
|
||||
self.progress_bar.setValue(int(float(kwargs['progress'])))
|
||||
|
||||
|
||||
def load_settings(self):
|
||||
"""
|
||||
Helper function that tries to load existing backup profile
|
||||
@ -260,24 +291,6 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
|
||||
|
||||
return True
|
||||
|
||||
def __do_backup__(self, t_monitor):
|
||||
msg = []
|
||||
|
||||
try:
|
||||
vm = self.qubes_app.domains[
|
||||
self.appvm_combobox.currentText()]
|
||||
if not vm.is_running():
|
||||
vm.start()
|
||||
self.qubes_app.qubesd_call(
|
||||
'dom0', 'admin.backup.Execute',
|
||||
backup_utils.get_profile_name(True))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
msg.append(str(ex))
|
||||
|
||||
if msg:
|
||||
t_monitor.set_error_msg('\n'.join(msg))
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
@staticmethod
|
||||
def cleanup_temporary_files():
|
||||
@ -310,33 +323,27 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
|
||||
self.showFileDialog.setChecked(self.showFileDialog.isEnabled()
|
||||
and str(self.dir_line_edit.text())
|
||||
.count("media/") > 0)
|
||||
self.thread_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(
|
||||
target=self.__do_backup__,
|
||||
args=(self.thread_monitor,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
while not self.thread_monitor.is_finished():
|
||||
self.qt_app.processEvents()
|
||||
time.sleep(0.1)
|
||||
vm = self.qubes_app.domains[
|
||||
self.appvm_combobox.currentText()]
|
||||
|
||||
if not self.thread_monitor.success:
|
||||
if self.canceled:
|
||||
self.progress_status.setText(
|
||||
self.tr(
|
||||
"Backup aborted. "
|
||||
"Temporary file may be left at backup location."))
|
||||
else:
|
||||
self.thread = BackupThread(vm)
|
||||
self.thread.finished.connect(self.backup_finished)
|
||||
self.thread.start()
|
||||
|
||||
signal.signal(signal.SIGCHLD, old_sigchld_handler)
|
||||
|
||||
def backup_finished(self):
|
||||
if self.thread.msg:
|
||||
self.progress_status.setText(self.tr("Backup error."))
|
||||
QtGui.QMessageBox.warning(
|
||||
self, self.tr("Backup error!"),
|
||||
self.tr("ERROR: {}").format(
|
||||
self.thread_monitor.error_msg))
|
||||
self.thread.msg))
|
||||
else:
|
||||
self.progress_bar.setMaximum(100)
|
||||
self.progress_bar.setValue(100)
|
||||
self.progress_status.setText(self.tr("Backup finished."))
|
||||
|
||||
if self.showFileDialog.isChecked():
|
||||
orig_text = self.progress_status.text
|
||||
self.progress_status.setText(
|
||||
@ -344,29 +351,26 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
|
||||
" Please unmount your backup volume and cancel "
|
||||
"the file selection dialog."))
|
||||
backup_utils.select_path_button_clicked(self, False, True)
|
||||
|
||||
self.button(self.CancelButton).setEnabled(False)
|
||||
self.button(self.FinishButton).setEnabled(True)
|
||||
self.showFileDialog.setEnabled(False)
|
||||
self.cleanup_temporary_files()
|
||||
|
||||
# turn off only when backup was successful
|
||||
if self.thread_monitor.success and \
|
||||
self.turn_off_checkbox.isChecked():
|
||||
if self.turn_off_checkbox.isChecked():
|
||||
os.system('systemctl poweroff')
|
||||
|
||||
signal.signal(signal.SIGCHLD, old_sigchld_handler)
|
||||
|
||||
def reject(self):
|
||||
if self.currentPage() is self.commit_page:
|
||||
self.canceled = True
|
||||
self.thread.terminate()
|
||||
self.qubes_app.qubesd_call(
|
||||
'dom0', 'admin.backup.Cancel',
|
||||
backup_utils.get_profile_name(True))
|
||||
self.progress_bar.setMaximum(100)
|
||||
self.progress_bar.setValue(0)
|
||||
self.button(self.CancelButton).setDisabled(True)
|
||||
self.cleanup_temporary_files()
|
||||
else:
|
||||
QtGui.QMessageBox.warning(
|
||||
self, self.tr("Backup aborted!"),
|
||||
self.tr("ERROR: {}").format("Aborted!"))
|
||||
|
||||
self.cleanup_temporary_files()
|
||||
self.done(0)
|
||||
|
||||
@ -402,9 +406,14 @@ def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
error + "at <b>line %d</b> of file <b>%s</b>.<br/><br/>"
|
||||
% (line, filename))
|
||||
|
||||
def loop_shutdown():
|
||||
pending = asyncio.Task.all_tasks()
|
||||
for task in pending:
|
||||
with suppress(asyncio.CancelledError):
|
||||
task.cancel()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
qt_app = QtGui.QApplication(sys.argv)
|
||||
qt_app.setOrganizationName("The Qubes Project")
|
||||
qt_app.setOrganizationDomain("http://qubes-os.org")
|
||||
@ -412,14 +421,24 @@ def main():
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
app = Qubes()
|
||||
qubes_app = Qubes()
|
||||
|
||||
backup_window = BackupVMsWindow(qt_app, app)
|
||||
loop = quamash.QEventLoop(qt_app)
|
||||
asyncio.set_event_loop(loop)
|
||||
dispatcher = events.EventsDispatcher(qubes_app)
|
||||
|
||||
backup_window = BackupVMsWindow(qt_app, qubes_app, dispatcher)
|
||||
backup_window.show()
|
||||
|
||||
qt_app.exec_()
|
||||
qt_app.exit()
|
||||
try:
|
||||
loop.run_until_complete(
|
||||
asyncio.ensure_future(dispatcher.listen_for_events()))
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
except Exception: # pylint: disable=broad-except
|
||||
loop_shutdown()
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()[:3]
|
||||
handle_exception(exc_type, exc_value, exc_traceback)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
51
qubesmanager/common_threads.py
Normal file
51
qubesmanager/common_threads.py
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/python2
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
from PyQt4 import QtCore # pylint: disable=import-error
|
||||
from qubesadmin import exc
|
||||
|
||||
|
||||
class RemoveVMThread(QtCore.QThread):
|
||||
def __init__(self, vm):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.vm = vm
|
||||
self.error = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
del self.vm.app.domains[self.vm.name]
|
||||
except exc.QubesException as ex:
|
||||
self.error("Error removing Qube!", str(ex))
|
||||
|
||||
|
||||
class CloneVMThread(QtCore.QThread):
|
||||
def __init__(self, src_vm, dst_name):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.src_vm = src_vm
|
||||
self.dst_name = dst_name
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
dst_vm = self.src_vm.app.clone_vm(self.src_vm, self.dst_name)
|
||||
self.error = ("Sucess", "The qube was cloned sucessfully.")
|
||||
except exc.QubesException as ex:
|
||||
self.error = ("Error while cloning Qube!", str(ex))
|
@ -35,7 +35,39 @@ import qubesadmin.exc
|
||||
from . import utils
|
||||
|
||||
from .ui_newappvmdlg import Ui_NewVMDlg # pylint: disable=import-error
|
||||
from .thread_monitor import ThreadMonitor
|
||||
|
||||
class CreateVMThread(QtCore.QThread):
|
||||
def __init__(self, app, vmclass, name, label, template, properties):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.app = app
|
||||
self.vmclass = vmclass
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.template = template
|
||||
self.properties = properties
|
||||
self.error = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self.vmclass == 'StandaloneVM' and self.template is not None:
|
||||
if self.template is qubesadmin.DEFAULT:
|
||||
src_vm = self.app.default_template
|
||||
else:
|
||||
src_vm = self.template
|
||||
vm = self.app.clone_vm(src_vm, self.name, self.vmclass)
|
||||
vm.label = self.label
|
||||
for k, v in self.properties.items():
|
||||
setattr(vm, k, v)
|
||||
else:
|
||||
vm = self.app.add_new_vm(self.vmclass,
|
||||
name=self.name, label=self.label, template=self.template)
|
||||
for k, v in self.properties.items():
|
||||
setattr(vm, k, v)
|
||||
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
self.error = str(qex)
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
self.error = repr(ex)
|
||||
|
||||
|
||||
class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
|
||||
@ -125,67 +157,36 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
|
||||
properties['virt_mode'] = 'hvm'
|
||||
properties['kernel'] = None
|
||||
|
||||
thread_monitor = ThreadMonitor()
|
||||
thread = threading.Thread(target=self.do_create_vm,
|
||||
args=(self.app, vmclass, name, label, template, properties,
|
||||
thread_monitor))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
self.thread = CreateVMThread(self.app, vmclass, name, label,
|
||||
template, properties)
|
||||
self.thread.finished.connect(self.create_finished)
|
||||
self.thread.start()
|
||||
|
||||
progress = QtGui.QProgressDialog(
|
||||
self.progress = QtGui.QProgressDialog(
|
||||
self.tr("Creating new qube <b>{}</b>...").format(name), "", 0, 0)
|
||||
progress.setCancelButton(None)
|
||||
progress.setModal(True)
|
||||
progress.show()
|
||||
self.progress.setCancelButton(None)
|
||||
self.progress.setModal(True)
|
||||
self.progress.show()
|
||||
|
||||
while not thread_monitor.is_finished():
|
||||
self.qtapp.processEvents()
|
||||
time.sleep(0.1)
|
||||
def create_finished(self):
|
||||
self.progress.hide()
|
||||
|
||||
progress.hide()
|
||||
|
||||
if not thread_monitor.success:
|
||||
if self.thread.error:
|
||||
QtGui.QMessageBox.warning(None,
|
||||
self.tr("Error creating the qube!"),
|
||||
self.tr("ERROR: {}").format(thread_monitor.error_msg))
|
||||
self.tr("ERROR: {}").format(thread.error))
|
||||
|
||||
self.done(0)
|
||||
|
||||
if thread_monitor.success:
|
||||
if not self.thread.error:
|
||||
if self.launch_settings.isChecked():
|
||||
subprocess.check_call(['qubes-vm-settings', name])
|
||||
subprocess.check_call(['qubes-vm-settings', str(self.name)])
|
||||
if self.install_system.isChecked():
|
||||
subprocess.check_call(
|
||||
['qubes-vm-boot-from-device', name])
|
||||
['qubes-vm-boot-from-device', srt(self.name)])
|
||||
|
||||
@staticmethod
|
||||
def do_create_vm(app, vmclass, name, label, template, properties,
|
||||
thread_monitor):
|
||||
try:
|
||||
if vmclass == 'StandaloneVM' and template is not None:
|
||||
if template is qubesadmin.DEFAULT:
|
||||
src_vm = app.default_template
|
||||
else:
|
||||
src_vm = template
|
||||
vm = app.clone_vm(src_vm, name, vmclass)
|
||||
vm.label = label
|
||||
for k, v in properties.items():
|
||||
setattr(vm, k, v)
|
||||
else:
|
||||
vm = app.add_new_vm(vmclass,
|
||||
name=name, label=label, template=template)
|
||||
for k, v in properties.items():
|
||||
setattr(vm, k, v)
|
||||
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
thread_monitor.set_error_msg(str(qex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
thread_monitor.set_error_msg(repr(ex))
|
||||
|
||||
thread_monitor.set_finished()
|
||||
|
||||
def type_change(self):
|
||||
|
||||
# AppVM
|
||||
if self.vm_type.currentIndex() == 0:
|
||||
self.template_vm.setEnabled(True)
|
||||
|
@ -25,12 +25,13 @@ import sys
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
import traceback
|
||||
from contextlib import suppress
|
||||
import time
|
||||
|
||||
import quamash
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
|
||||
from qubesadmin import Qubes
|
||||
from qubesadmin import exc
|
||||
@ -51,6 +52,7 @@ from . import backup
|
||||
from . import create_new_vm
|
||||
from . import log_dialog
|
||||
from . import utils as manager_utils
|
||||
from . import common_threads
|
||||
|
||||
|
||||
class SearchBox(QtGui.QLineEdit):
|
||||
@ -241,32 +243,20 @@ class StartVMThread(QtCore.QThread):
|
||||
def __init__(self, vm):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.vm = vm
|
||||
self.error = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.vm.start()
|
||||
except exc.QubesException as ex:
|
||||
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
|
||||
"Error starting Qube!", str(ex))
|
||||
self.error = ("Error starting Qube!", str(ex))
|
||||
|
||||
|
||||
class RemoveVMThread(QtCore.QThread):
|
||||
def __init__(self, vm, qubes_app):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.vm = vm
|
||||
self.qubes_app = qubes_app
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
del self.qubes_app.domains[self.vm.name]
|
||||
except exc.QubesException as ex:
|
||||
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
|
||||
"Error removing Qube!", str(ex))
|
||||
|
||||
class UpdateVMThread(QtCore.QThread):
|
||||
def __init__(self, vm):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.vm = vm
|
||||
self.error = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
@ -279,23 +269,7 @@ class UpdateVMThread(QtCore.QThread):
|
||||
self.vm.run_service("qubes.InstallUpdatesGUI",\
|
||||
user="root", wait=False)
|
||||
except (ChildProcessError, exc.QubesException) as ex:
|
||||
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
|
||||
"Error on Qube update!", str(ex))
|
||||
|
||||
|
||||
class CloneVMThread(QtCore.QThread):
|
||||
def __init__(self, src_vm, qubes_app, dst_name):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.src_vm = src_vm
|
||||
self.qubes_app = qubes_app
|
||||
self.dst_name = dst_name
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
dst_vm = self.qubes_app.clone_vm(self.src_vm, self.dst_name)
|
||||
except exc.QubesException as ex:
|
||||
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
|
||||
"Error while cloning Qube!", str(ex))
|
||||
self.error = ("Error on Qube update!", str(ex))
|
||||
|
||||
|
||||
class RunCommandThread(QtCore.QThread):
|
||||
@ -308,8 +282,7 @@ class RunCommandThread(QtCore.QThread):
|
||||
try:
|
||||
self.vm.run(self.command_to_run)
|
||||
except (ChildProcessError, exc.QubesException) as ex:
|
||||
self.emit(QtCore.SIGNAL('show_error(QString, QString)'),\
|
||||
"Error while running command!", str(ex))
|
||||
self.error = ("Error while running command!", str(ex))
|
||||
|
||||
|
||||
class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
@ -472,7 +445,10 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
dispatcher.add_handler('domain-start-failed',
|
||||
self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-stopped', self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-pre-shutdown', self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-shutdown', self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-paused', self.on_domain_status_changed)
|
||||
dispatcher.add_handler('domain-unpaused', self.on_domain_status_changed)
|
||||
|
||||
dispatcher.add_handler('domain-add', self.on_domain_added)
|
||||
dispatcher.add_handler('domain-delete', self.on_domain_removed)
|
||||
@ -496,6 +472,12 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
def clear_threads(self):
|
||||
for thread in self.threads_list:
|
||||
if thread.isFinished():
|
||||
if thread.error:
|
||||
(title, msg) = thread.error
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr(title),
|
||||
self.tr("ERROR: {0}").format(msg))
|
||||
self.threads_list.remove(thread)
|
||||
|
||||
def closeEvent(self, event):
|
||||
@ -659,7 +641,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
self.manager_settings.sync()
|
||||
|
||||
def table_selection_changed(self):
|
||||
|
||||
vm = self.get_selected_vm()
|
||||
|
||||
if vm is not None and vm in self.qubes_app.domains:
|
||||
@ -783,7 +764,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
|
||||
else:
|
||||
# remove the VM
|
||||
thread = RemoveVMThread(vm, self.qubes_app)
|
||||
thread = common_threads.RemoveVMThread(vm)
|
||||
self.threads_list.append(thread)
|
||||
self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error)
|
||||
thread.finished.connect(self.clear_threads)
|
||||
@ -806,16 +787,10 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
if not ok or clone_name == "":
|
||||
return
|
||||
|
||||
self.thread = CloneVMThread(vm, self.qubes_app, clone_name)
|
||||
self.thread.start()
|
||||
return
|
||||
|
||||
progress = QtGui.QProgressDialog(
|
||||
self.tr("Cloning Qube <b>{0}</b> to <b>{1}</b>...").format(
|
||||
vm.name, clone_name), "", 0, 0)
|
||||
progress.setCancelButton(None)
|
||||
progress.setModal(True)
|
||||
progress.show()
|
||||
thread = common_threads.CloneVMThread(vm, clone_name)
|
||||
thread.finished.connect(self.clear_threads)
|
||||
self.threads_list.append(thread)
|
||||
thread.start()
|
||||
|
||||
# noinspection PyArgumentList
|
||||
@QtCore.pyqtSlot(name='on_action_resumevm_triggered')
|
||||
@ -825,8 +800,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
if vm.get_power_state() in ["Paused", "Suspended"]:
|
||||
try:
|
||||
vm.unpause()
|
||||
self.vms_in_table[vm.qid].update()
|
||||
self.table_selection_changed()
|
||||
except exc.QubesException as ex:
|
||||
QtGui.QMessageBox.warning(
|
||||
None, self.tr("Error unpausing Qube!"),
|
||||
@ -841,7 +814,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
|
||||
thread = StartVMThread(vm)
|
||||
self.threads_list.append(thread)
|
||||
self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error)
|
||||
thread.finished.connect(self.clear_threads)
|
||||
thread.start()
|
||||
|
||||
@ -1016,7 +988,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
|
||||
thread = UpdateVMThread(vm)
|
||||
self.threads_list.append(thread)
|
||||
self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error)
|
||||
thread.finished.connect(self.clear_threads)
|
||||
thread.start()
|
||||
|
||||
@ -1035,11 +1006,9 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
|
||||
thread = RunCommandThread(vm, command_to_run)
|
||||
self.threads_list.append(thread)
|
||||
self.connect(thread, QtCore.SIGNAL("show_error(QString, QString)"), self.show_error)
|
||||
thread.finished.connect(self.clear_threads)
|
||||
thread.start()
|
||||
|
||||
|
||||
# noinspection PyArgumentList
|
||||
@QtCore.pyqtSlot(name='on_action_set_keyboard_layout_triggered')
|
||||
def action_set_keyboard_layout_triggered(self):
|
||||
@ -1079,7 +1048,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
||||
# noinspection PyArgumentList
|
||||
@QtCore.pyqtSlot(name='on_action_backup_triggered')
|
||||
def action_backup_triggered(self):
|
||||
backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app)
|
||||
backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, self.dispatcher)
|
||||
backup_window.show()
|
||||
|
||||
# noinspection PyArgumentList
|
||||
|
@ -38,7 +38,6 @@ from qubes import backup
|
||||
from . import ui_restoredlg # pylint: disable=no-name-in-module
|
||||
from . import multiselectwidget
|
||||
from . import backup_utils
|
||||
from . import thread_monitor
|
||||
|
||||
from multiprocessing import Queue, Event
|
||||
from multiprocessing.queues import Empty
|
||||
@ -46,8 +45,37 @@ from qubesadmin import Qubes, exc
|
||||
from qubesadmin.backup import restore
|
||||
|
||||
|
||||
class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
||||
class RestoreThread(QtCore.QThread):
|
||||
def __init__(self, backup_restore, vms_to_restore):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.backup_restore = backup_restore
|
||||
self.vms_to_restore = vms_to_restore
|
||||
self.error = None
|
||||
self.msg = None
|
||||
|
||||
def run(self):
|
||||
err_msg = []
|
||||
try:
|
||||
self.backup_restore.restore_do(self.vms_to_restore)
|
||||
|
||||
except backup.BackupCanceledError as ex:
|
||||
self.canceled = True
|
||||
err_msg.append(str(ex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
err_msg.append(str(ex))
|
||||
err_msg.append(
|
||||
self.tr("Partially restored files left in /var/tmp/restore_*, "
|
||||
"investigate them and/or clean them up"))
|
||||
if err_msg:
|
||||
self.error = '\n'.join(err_msg)
|
||||
self.msg = '<b><font color="red">{0}</font></b>'.format(
|
||||
self.tr("Finished with errors!"))
|
||||
else:
|
||||
self.msg = '<font color="green">{0}</font>'.format(
|
||||
self.tr("Finished successfully!"))
|
||||
|
||||
|
||||
class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
||||
def __init__(self, qt_app, qubes_app, parent=None):
|
||||
super(RestoreVMsWindow, self).__init__(parent)
|
||||
|
||||
@ -64,9 +92,6 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
self.canceled = False
|
||||
self.error_detected = Event()
|
||||
self.thread_monitor = None
|
||||
self.backup_restore = None
|
||||
self.target_appvm = None
|
||||
|
||||
@ -148,37 +173,8 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
||||
def append_output(self, text):
|
||||
self.commit_text_edit.append(text)
|
||||
|
||||
def __do_restore__(self, t_monitor):
|
||||
err_msg = []
|
||||
try:
|
||||
self.backup_restore.restore_do(self.vms_to_restore)
|
||||
|
||||
except backup.BackupCanceledError as ex:
|
||||
self.canceled = True
|
||||
err_msg.append(str(ex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
err_msg.append(str(ex))
|
||||
err_msg.append(
|
||||
self.tr("Partially restored files left in /var/tmp/restore_*, "
|
||||
"investigate them and/or clean them up"))
|
||||
|
||||
if self.canceled:
|
||||
self.append_output('<b><font color="red">{0}</font></b>'.format(
|
||||
self.tr("Restore aborted!")))
|
||||
elif err_msg or self.error_detected.is_set():
|
||||
if err_msg:
|
||||
t_monitor.set_error_msg('\n'.join(err_msg))
|
||||
self.append_output('<b><font color="red">{0}</font></b>'.format(
|
||||
self.tr("Finished with errors!")))
|
||||
else:
|
||||
self.append_output('<font color="green">{0}</font>'.format(
|
||||
self.tr("Finished successfully!")))
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
def current_page_changed(self, page_id): # pylint: disable=unused-argument
|
||||
|
||||
old_sigchld_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
self.old_sigchld_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
if self.currentPage() is self.select_vms_page:
|
||||
self.__fill_vms_list__()
|
||||
|
||||
@ -210,14 +206,43 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
||||
and str(self.dir_line_edit.text())
|
||||
.count("media/") > 0)
|
||||
|
||||
self.thread_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(target=self.__do_restore__,
|
||||
args=(self.thread_monitor,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
while not self.thread_monitor.is_finished():
|
||||
self.qt_app.processEvents()
|
||||
time.sleep(0.1)
|
||||
self.thread = RestoreThread(self.backup_restore, self.vms_to_restore)
|
||||
self.thread.finished.connect(self.thread_finished)
|
||||
|
||||
# Start log timer
|
||||
timer = QtCore.QTimer(self)
|
||||
timer.timeout.connect(self.update_log)
|
||||
timer.start(1000)
|
||||
|
||||
self.thread.start()
|
||||
|
||||
def thread_finished(self):
|
||||
self.progress_bar.setMaximum(100)
|
||||
self.progress_bar.setValue(100)
|
||||
|
||||
if self.thread.error:
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Backup error!"),
|
||||
self.tr("ERROR: {0}").format(
|
||||
self.thread.error))
|
||||
|
||||
if self.thread.msg:
|
||||
self.append_output(self.thread.msg)
|
||||
|
||||
if self.showFileDialog.isChecked():
|
||||
self.append_output(
|
||||
'<b><font color="black">{0}</font></b>'.format(
|
||||
self.tr("Please unmount your backup volume and cancel "
|
||||
"the file selection dialog.")))
|
||||
backup_utils.select_path_button_clicked(self, False, True)
|
||||
|
||||
self.button(self.FinishButton).setEnabled(True)
|
||||
self.button(self.CancelButton).setEnabled(False)
|
||||
self.showFileDialog.setEnabled(False)
|
||||
signal.signal(signal.SIGCHLD, self.old_sigchld_handler)
|
||||
|
||||
def update_log(self):
|
||||
try:
|
||||
log_record = self.feedback_queue.get_nowait()
|
||||
while log_record:
|
||||
@ -232,29 +257,6 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
||||
except Empty:
|
||||
pass
|
||||
|
||||
if not self.thread_monitor.success:
|
||||
if not self.canceled:
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Backup error!"),
|
||||
self.tr("ERROR: {0}").format(
|
||||
self.thread_monitor.error_msg))
|
||||
self.progress_bar.setMaximum(100)
|
||||
self.progress_bar.setValue(100)
|
||||
|
||||
if self.showFileDialog.isChecked():
|
||||
self.append_output(
|
||||
'<b><font color="black">{0}</font></b>'.format(
|
||||
self.tr("Please unmount your backup volume and cancel "
|
||||
"the file selection dialog.")))
|
||||
self.qt_app.processEvents()
|
||||
backup_utils.select_path_button_clicked(self, False, True)
|
||||
|
||||
self.button(self.FinishButton).setEnabled(True)
|
||||
self.button(self.CancelButton).setEnabled(False)
|
||||
self.showFileDialog.setEnabled(False)
|
||||
|
||||
signal.signal(signal.SIGCHLD, old_sigchld_handler)
|
||||
|
||||
def all_vms_good(self):
|
||||
for vm_info in self.vms_to_restore.values():
|
||||
@ -265,11 +267,13 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
if self.currentPage() is self.commit_page:
|
||||
if self.currentPage() is self.commit_page and self.thread.isRunning():
|
||||
self.backup_restore.canceled = True
|
||||
self.append_output('<font color="red">{0}</font>'.format(
|
||||
self.tr("Aborting the operation...")))
|
||||
self.button(self.CancelButton).setDisabled(True)
|
||||
|
||||
self.thread.terminate()
|
||||
else:
|
||||
self.done(0)
|
||||
|
||||
|
@ -39,7 +39,7 @@ import qubesadmin.exc
|
||||
|
||||
from . import utils
|
||||
from . import multiselectwidget
|
||||
from . import thread_monitor
|
||||
from . import common_threads
|
||||
from . import device_list
|
||||
|
||||
from .appmenu_select import AppmenuSelectManager
|
||||
@ -49,6 +49,77 @@ from PyQt4 import QtCore, QtGui # pylint: disable=import-error
|
||||
from . import ui_settingsdlg # pylint: disable=no-name-in-module
|
||||
|
||||
|
||||
class RenameVMThread(QtCore.QThread):
|
||||
def __init__(self, vm, new_vm_name, dependencies):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.vm = vm
|
||||
self.new_vm_name = new_vm_name
|
||||
self.dependencies = dependencies
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
new_vm = self.vm.app.clone_vm(self.vm, self.new_vm_name)
|
||||
|
||||
failed_props = []
|
||||
|
||||
for (holder, prop) in self.dependencies:
|
||||
try:
|
||||
if holder is None:
|
||||
setattr(self.vm.app, prop, new_vm)
|
||||
else:
|
||||
setattr(holder, prop, new_vm)
|
||||
except exc.QubesException as qex:
|
||||
failed_props += [(holder, prop)]
|
||||
|
||||
if not failed_props:
|
||||
del self.vm.app.domains[self.vm.name]
|
||||
else:
|
||||
list_text = utils.format_dependencies_list(failed_props)
|
||||
|
||||
QtGui.QMessageBox.warning(
|
||||
self,
|
||||
self.tr("Warning: rename partially unsuccessful"),
|
||||
self.tr("Some properties could not be changed to the new "
|
||||
"name. The system has now both {} and {} qubes. "
|
||||
"To resolve this, please check and change the "
|
||||
"following properties and remove the qube {} "
|
||||
"manually.<br> ").format(
|
||||
self.vm.name, name, self.vm.name) + list_text)
|
||||
|
||||
except exc.QubesException as qex:
|
||||
self.error = ("Rename error!", str(ex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
self.error = ("Rename error!", repr(ex))
|
||||
|
||||
|
||||
class RefreshAppsVMThread(QtCore.QThread):
|
||||
def __init__(self, vm):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.error = None
|
||||
self.vm = vm
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
try:
|
||||
target_vm = self.vm.template
|
||||
except AttributeError:
|
||||
target_vm = self.vm
|
||||
|
||||
if not target_vm.is_running():
|
||||
not_running = True
|
||||
target_vm.start()
|
||||
else:
|
||||
not_running = False
|
||||
|
||||
subprocess.check_call(['qvm-sync-appmenus', target_vm.name])
|
||||
|
||||
if not_running:
|
||||
target_vm.shutdown()
|
||||
|
||||
except Exception as ex:
|
||||
self.error = ("Refresh failed!", str(ex))
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
tabs_indices = collections.OrderedDict((
|
||||
@ -65,6 +136,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
|
||||
self.vm = vm
|
||||
self.qapp = qapp
|
||||
self.threads_list = []
|
||||
try:
|
||||
self.source_vm = self.vm.template
|
||||
except AttributeError:
|
||||
@ -150,6 +222,18 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
self.refresh_apps_button.clicked.connect(
|
||||
self.refresh_apps_button_pressed)
|
||||
|
||||
def clear_threads(self):
|
||||
for thread in self.threads_list:
|
||||
if thread.isFinished():
|
||||
if thread.error:
|
||||
(title, msg) = thread.error
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr(title),
|
||||
self.tr("ERROR: {0}").format(msg))
|
||||
|
||||
self.threads_list.remove(thread)
|
||||
|
||||
def keyPressEvent(self, event): # pylint: disable=invalid-name
|
||||
if event.key() == QtCore.Qt.Key_Enter \
|
||||
or event.key() == QtCore.Qt.Key_Return:
|
||||
@ -164,31 +248,14 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
pass
|
||||
|
||||
def save_changes(self):
|
||||
t_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(target=self.__save_changes__,
|
||||
args=(t_monitor,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
error = self.__save_changes__()
|
||||
|
||||
progress = QtGui.QProgressDialog(
|
||||
self.tr("Applying settings to <b>{0}</b>...").format(self.vm.name),
|
||||
"", 0, 0)
|
||||
progress.setCancelButton(None)
|
||||
progress.setModal(True)
|
||||
progress.show()
|
||||
|
||||
while not t_monitor.is_finished():
|
||||
self.qapp.processEvents()
|
||||
time.sleep(0.1)
|
||||
|
||||
progress.hide()
|
||||
|
||||
if not t_monitor.success:
|
||||
if error:
|
||||
QtGui.QMessageBox.warning(
|
||||
self,
|
||||
self.tr("Error while changing settings for {0}!"
|
||||
).format(self.vm.name),
|
||||
self.tr("ERROR: {0}").format(t_monitor.error_msg))
|
||||
self.tr("ERROR: {0}").format('\n'.join(ret)))
|
||||
|
||||
def apply(self):
|
||||
self.save_changes()
|
||||
@ -197,9 +264,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
self.save_changes()
|
||||
self.done(0)
|
||||
|
||||
def __save_changes__(self, t_monitor):
|
||||
|
||||
def __save_changes__(self):
|
||||
ret = []
|
||||
|
||||
try:
|
||||
ret_tmp = self.__apply_basic_tab__()
|
||||
if ret_tmp:
|
||||
@ -237,12 +304,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
ret += [self.tr("Applications tab:"), repr(ex)]
|
||||
|
||||
if ret:
|
||||
t_monitor.set_error_msg('\n'.join(ret))
|
||||
|
||||
utils.debug('\n'.join(ret))
|
||||
|
||||
t_monitor.set_finished()
|
||||
return ret
|
||||
|
||||
def check_network_availability(self):
|
||||
netvm = self.vm.netvm
|
||||
@ -464,61 +527,6 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
"allowed value."))
|
||||
self.init_mem.setValue(self.max_mem_size.value() / 10)
|
||||
|
||||
def _run_in_thread(self, func, *args):
|
||||
t_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(target=func, args=(t_monitor, *args,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
while not t_monitor.is_finished():
|
||||
self.qapp.processEvents()
|
||||
time.sleep(0.1)
|
||||
|
||||
if not t_monitor.success:
|
||||
QtGui.QMessageBox.warning(self,
|
||||
self.tr("Error!"),
|
||||
self.tr("ERROR: {}").format(
|
||||
t_monitor.error_msg))
|
||||
return False
|
||||
return True
|
||||
|
||||
def _rename_vm(self, t_monitor, name, dependencies):
|
||||
try:
|
||||
new_vm = self.vm.app.clone_vm(self.vm, name)
|
||||
|
||||
failed_props = []
|
||||
|
||||
for (holder, prop) in dependencies:
|
||||
try:
|
||||
if holder is None:
|
||||
setattr(self.vm.app, prop, new_vm)
|
||||
else:
|
||||
setattr(holder, prop, new_vm)
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
failed_props += [(holder, prop)]
|
||||
|
||||
if not failed_props:
|
||||
del self.vm.app.domains[self.vm.name]
|
||||
else:
|
||||
list_text = utils.format_dependencies_list(failed_props)
|
||||
|
||||
QtGui.QMessageBox.warning(
|
||||
self,
|
||||
self.tr("Warning: rename partially unsuccessful"),
|
||||
self.tr("Some properties could not be changed to the new "
|
||||
"name. The system has now both {} and {} qubes. "
|
||||
"To resolve this, please check and change the "
|
||||
"following properties and remove the qube {} "
|
||||
"manually.<br> ").format(
|
||||
self.vm.name, name, self.vm.name) + list_text)
|
||||
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
t_monitor.set_error_msg(str(qex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
t_monitor.set_error_msg(repr(ex))
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
def rename_vm(self):
|
||||
|
||||
dependencies = admin_utils.vm_dependencies(self.vm.app, self.vm)
|
||||
@ -544,19 +552,12 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
self.tr('New name: (WARNING: all other changes will be discarded)'))
|
||||
|
||||
if ok:
|
||||
if self._run_in_thread(self._rename_vm, new_vm_name, dependencies):
|
||||
self.done(0)
|
||||
|
||||
def _remove_vm(self, t_monitor):
|
||||
try:
|
||||
del self.vm.app.domains[self.vm.name]
|
||||
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
t_monitor.set_error_msg(str(qex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
t_monitor.set_error_msg(repr(ex))
|
||||
|
||||
t_monitor.set_finished()
|
||||
thread = RenameVMThread(self.vm, new_vm_name, dependencies)
|
||||
self.threads_list.append(thread)
|
||||
thread.finished.connect(self.clear_threads)
|
||||
thread.start()
|
||||
thread.wait()
|
||||
#self.done(0)
|
||||
|
||||
def remove_vm(self):
|
||||
|
||||
@ -584,7 +585,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
'qube\'s name below.'))
|
||||
|
||||
if ok and answer == self.vm.name:
|
||||
self._run_in_thread(self._remove_vm)
|
||||
thread = common_threads.RemoveVMThread(self.vm)
|
||||
thread.start()
|
||||
self.done(0)
|
||||
|
||||
elif ok:
|
||||
@ -612,11 +614,10 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
self.tr('Name for the cloned qube:'))
|
||||
|
||||
if ok:
|
||||
self._run_in_thread(self._clone_vm, cloned_vm_name)
|
||||
QtGui.QMessageBox.warning(
|
||||
self,
|
||||
self.tr("Success"),
|
||||
self.tr("The qube was cloned successfully."))
|
||||
thread = common_threads.CloneVMThread(self.vm, cloned_vm_name)
|
||||
thread.finished.connect(self.clear_threads)
|
||||
self.threads_list.append(thread)
|
||||
thread.start()
|
||||
|
||||
######### advanced tab
|
||||
|
||||
@ -944,43 +945,19 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
|
||||
######## applications tab
|
||||
|
||||
def refresh_apps_in_vm(self, t_monitor):
|
||||
try:
|
||||
target_vm = self.vm.template
|
||||
except AttributeError:
|
||||
target_vm = self.vm
|
||||
|
||||
if not target_vm.is_running():
|
||||
not_running = True
|
||||
target_vm.start()
|
||||
else:
|
||||
not_running = False
|
||||
|
||||
subprocess.check_call(['qvm-sync-appmenus', target_vm.name])
|
||||
|
||||
if not_running:
|
||||
target_vm.shutdown()
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
def refresh_apps_button_pressed(self):
|
||||
|
||||
self.refresh_apps_button.setEnabled(False)
|
||||
self.refresh_apps_button.setText(self.tr('Refresh in progress...'))
|
||||
|
||||
t_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(
|
||||
target=self.refresh_apps_in_vm,
|
||||
args=(t_monitor,))
|
||||
thread.daemon = True
|
||||
thread = RefreshAppsVMThread(self.vm)
|
||||
thread.finished.connect(self.clear_threads)
|
||||
thread.finished.connect(self.refresh_finished)
|
||||
self.threads_list.append(thread)
|
||||
thread.start()
|
||||
|
||||
while not t_monitor.is_finished():
|
||||
self.qapp.processEvents()
|
||||
time.sleep(0.1)
|
||||
|
||||
def refresh_finished(self):
|
||||
self.app_list_manager = AppmenuSelectManager(self.vm, self.app_list)
|
||||
|
||||
self.refresh_apps_button.setEnabled(True)
|
||||
self.refresh_apps_button.setText(self.tr('Refresh Applications'))
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
import PyQt4.QtCore # pylint: disable=import-error
|
||||
|
||||
import threading
|
||||
|
||||
class ThreadMonitor(PyQt4.QtCore.QObject):
|
||||
def __init__(self):
|
||||
self.success = True
|
||||
self.error_msg = None
|
||||
self.event_finished = threading.Event()
|
||||
|
||||
def set_error_msg(self, error_msg):
|
||||
self.success = False
|
||||
self.error_msg = error_msg
|
||||
self.set_finished()
|
||||
|
||||
def is_finished(self):
|
||||
return self.event_finished.is_set()
|
||||
|
||||
def set_finished(self):
|
||||
self.event_finished.set()
|
Loading…
Reference in New Issue
Block a user