Merge remote-tracking branch 'origin/pr/210'

* origin/pr/210:
  Fixed translation for application names
  Added translation support to all untranslated qubesmanager files
  Re-enabled translation support for Qt5
This commit is contained in:
Marek Marczykowski-Górecki 2019-11-09 15:32:18 +01:00
commit 283ed2540c
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
19 changed files with 3903 additions and 1735 deletions

View File

@ -1,44 +1,56 @@
SOURCES = \ SOURCES = \
ui/about.ui \ ui/about.ui \
ui/backupdlg.ui \ ui/backupdlg.ui \
ui/bootfromdevice.ui \
ui/devicelist.ui \
ui/globalsettingsdlg.ui \ ui/globalsettingsdlg.ui \
ui/informationnotes.ui \
ui/logdlg.ui \ ui/logdlg.ui \
ui/mainwindow.ui \
ui/multiselectwidget.ui \ ui/multiselectwidget.ui \
ui/newappvmdlg.ui \ ui/newappvmdlg.ui \
ui/newfwruledlg.ui \ ui/newfwruledlg.ui \
ui/qubemanager.ui \
ui/releasenotes.ui \ ui/releasenotes.ui \
ui/restoredlg.ui \ ui/restoredlg.ui \
ui/settingsdlg.ui \ ui/settingsdlg.ui \
ui/templatemanager.ui \
qubesmanager/about.py \ qubesmanager/about.py \
qubesmanager/appmenu_select.py \ qubesmanager/appmenu_select.py \
qubesmanager/backup.py \ qubesmanager/backup.py \
qubesmanager/backup_utils.py \ qubesmanager/backup_utils.py \
qubesmanager/block.py \ qubesmanager/bootfromdevice.py \
qubesmanager/clipboard.py \ qubesmanager/clipboard.py \
qubesmanager/create_new_vm.py \
qubesmanager/common_threads.py \ qubesmanager/common_threads.py \
qubesmanager/create_new_vm.py \
qubesmanager/device_list.py \
qubesmanager/firewall.py \ qubesmanager/firewall.py \
qubesmanager/global_settings.py \ qubesmanager/global_settings.py \
qubesmanager/informationnotes.py \
qubesmanager/log_dialog.py \ qubesmanager/log_dialog.py \
qubesmanager/main.py \
qubesmanager/multiselectwidget.py \ qubesmanager/multiselectwidget.py \
qubesmanager/qube_manager.py \
qubesmanager/releasenotes.py \ qubesmanager/releasenotes.py \
qubesmanager/resources_rc.py \ qubesmanager/resources_rc.py \
qubesmanager/restore.py \ qubesmanager/restore.py \
qubesmanager/settings.py \ qubesmanager/settings.py \
qubesmanager/table_widgets.py \ qubesmanager/table_widgets.py \
qubesmanager/template_manager.py \
qubesmanager/ui_about.py \ qubesmanager/ui_about.py \
qubesmanager/ui_backupdlg.py \ qubesmanager/ui_backupdlg.py \
qubesmanager/ui_bootfromdevice.py \
qubesmanager/ui_devicelist.py \
qubesmanager/ui_globalsettingsdlg.py \ qubesmanager/ui_globalsettingsdlg.py \
qubesmanager/ui_informationnotes.py \
qubesmanager/ui_logdlg.py \ qubesmanager/ui_logdlg.py \
qubesmanager/ui_mainwindow.py \
qubesmanager/ui_multiselectwidget.py \ qubesmanager/ui_multiselectwidget.py \
qubesmanager/ui_newappvmdlg.py \ qubesmanager/ui_newappvmdlg.py \
qubesmanager/ui_newfwruledlg.py \ qubesmanager/ui_newfwruledlg.py \
qubesmanager/ui_qubemanager.py \
qubesmanager/ui_releasenotes.py \ qubesmanager/ui_releasenotes.py \
qubesmanager/ui_restoredlg.py \ qubesmanager/ui_restoredlg.py \
qubesmanager/ui_settingsdlg.py qubesmanager/ui_settingsdlg.py \
qubesmanager/ui_templatemanager.py \
qubesmanager/utils.py
TRANSLATIONS = \ TRANSLATIONS = \
qubesmanager/i18n/qubesmanager_en.ts \ qubesmanager/i18n/qubesmanager_en.ts \

View File

@ -20,13 +20,12 @@
# #
import subprocess import subprocess
import PyQt5.QtWidgets # pylint: disable=import-error from PyQt5 import QtWidgets, QtCore # pylint: disable=import-error
# TODO description in tooltip # TODO description in tooltip
# TODO icon # TODO icon
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
class AppListWidgetItem(PyQt5.QtWidgets.QListWidgetItem): class AppListWidgetItem(QtWidgets.QListWidgetItem):
def __init__(self, name, ident, tooltip=None, parent=None): def __init__(self, name, ident, tooltip=None, parent=None):
super(AppListWidgetItem, self).__init__(name, parent) super(AppListWidgetItem, self).__init__(name, parent)
if tooltip: if tooltip:
@ -85,6 +84,9 @@ class AppmenuSelectManager:
stdin=subprocess.PIPE) stdin=subprocess.PIPE)
p.communicate('\n'.join(new_whitelisted).encode()) p.communicate('\n'.join(new_whitelisted).encode())
if p.returncode != 0: if p.returncode != 0:
raise RuntimeError('qvm-appmenus --set-whitelist failed') exception_text = QtCore.QCoreApplication.translate(
"Command {command} failed", "exception").format(
command='qvm-appmenus --set-whitelist')
raise RuntimeError(exception_text)
return True return True

View File

@ -24,7 +24,7 @@ import signal
from qubesadmin import exc from qubesadmin import exc
from qubesadmin import utils as admin_utils from qubesadmin import utils as admin_utils
from PyQt5 import QtCore, QtWidgets # pylint: disable=import-error from PyQt5 import QtCore, QtWidgets, QtGui # pylint: disable=import-error
from . import ui_backupdlg # pylint: disable=no-name-in-module from . import ui_backupdlg # pylint: disable=no-name-in-module
from . import multiselectwidget from . import multiselectwidget
@ -120,6 +120,10 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
self.dispatcher = dispatcher self.dispatcher = dispatcher
dispatcher.add_handler('backup-progress', self.on_backup_progress) dispatcher.add_handler('backup-progress', self.on_backup_progress)
def setup_application(self):
self.qt_app.setApplicationName(self.tr("Qubes Backup VMs"))
self.qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager"))
def on_backup_progress(self, __submitter, _event, **kwargs): def on_backup_progress(self, __submitter, _event, **kwargs):
self.progress_bar.setValue(int(float(kwargs['progress']))) self.progress_bar.setValue(int(float(kwargs['progress'])))
@ -323,9 +327,9 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
def backup_finished(self): def backup_finished(self):
if self.thread.msg: if self.thread.msg:
self.progress_status.setText(self.tr("Backup error.")) self.progress_status.setText(self.tr("Backup error"))
QtWidgets.QMessageBox.warning( QtWidgets.QMessageBox.warning(
self, self.tr("Backup error!"), self, self.tr("Backup error"),
self.tr("ERROR: {}").format( self.tr("ERROR: {}").format(
self.thread.msg)) self.thread.msg))
self.button(self.CancelButton).setEnabled(False) self.button(self.CancelButton).setEnabled(False)
@ -368,7 +372,7 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
self.thread.wait() self.thread.wait()
QtWidgets.QMessageBox.warning( QtWidgets.QMessageBox.warning(
self, self.tr("Backup aborted!"), self, self.tr("Backup aborted!"),
self.tr("ERROR: {}").format("Aborted!")) self.tr("ERROR: Aborted"))
self.cleanup_temporary_files() self.cleanup_temporary_files()
self.done(0) self.done(0)
@ -390,9 +394,7 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
def main(): def main():
utils.run_asynchronous("Qubes Backup VMs", utils.run_asynchronous(BackupVMsWindow)
"qubes-manager",
BackupVMsWindow)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,160 +0,0 @@
#!/usr/bin/python2
# -*- coding: utf8 -*-
# pylint: skip-file
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
#
# 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 threading
import time
from PyQt4.QtGui import QMessageBox
from qubes import qubesutils
class QubesBlockDevicesManager():
def __init__(self, qvm_collection):
self.qvm_collection = qvm_collection
self.attached_devs = {}
self.free_devs = {}
self.current_blk = {}
self.current_attached = {}
self.devs_changed = False
self.last_update_time = time.time()
self.blk_state_changed = True
self.msg = []
self.check_counter = 0
self.blk_lock = threading.Lock()
self.tray_message_func = None
self.update()
def block_devs_event(self, xid):
now = time.time()
#don't update more often than 1/10 s
if now - self.last_update_time >= 0.1:
self.last_update_time = now
self.blk_lock.acquire()
self.blk_state_changed = True
self.blk_lock.release()
def check_for_updates(self):
self.blk_lock.acquire()
ret = (self.blk_state_changed, self.msg)
if self.blk_state_changed == True:
self.check_counter += 1
self.update()
ret = (self.blk_state_changed, self.msg)
#let the update last for 3 manager-update cycles
if self.check_counter == 3:
self.check_counter = 0
self.blk_state_changed = False
self.msg = []
self.blk_lock.release()
return ret
def update(self):
blk = qubesutils.block_list(self.qvm_collection)
for b in blk:
att = qubesutils.block_check_attached(self.qvm_collection, blk[b])
if b in self.current_blk:
if blk[b] == self.current_blk[b]:
if self.current_attached[b] != att: #devices the same, sth with attaching changed
self.current_attached[b] = att
else: #device changed ?!
self.current_blk[b] = blk[b]
self.current_attached[b] = att
else: #new device
self.current_blk[b] = blk[b]
self.current_attached[b] = att
self.msg.append("Attached new device to <b>{}</b>: {}".format(
blk[b]['vm'], blk[b]['device']))
to_delete = []
for b in self.current_blk: #remove devices that are not there anymore
if b not in blk:
to_delete.append(b)
self.msg.append("Detached device from <b>{}</b>: {}".format(
self.current_blk[b]['vm'],
self.current_blk[b]['device']))
for d in to_delete:
del self.current_blk[d]
del self.current_attached[d]
self.__update_blk_entries__()
def __update_blk_entries__(self):
self.free_devs.clear()
self.attached_devs.clear()
for b in self.current_attached:
if self.current_attached[b]:
self.attached_devs[b] = self.__make_entry__(b, self.current_blk[b], self.current_attached[b])
else:
self.free_devs[b] = self.__make_entry__(b, self.current_blk[b], None)
def __make_entry__(self, k, dev, att):
size_str = qubesutils.bytes_to_kmg(dev['size'])
entry = { 'dev': dev['device'],
'dev_obj': dev,
'backend_name': dev['vm'],
'desc': dev['desc'],
'mode': dev['mode'],
'size': size_str,
'attached_to': att, }
return entry
def attach_device(self, vm, dev):
mode = self.free_devs[dev]['mode']
if self.tray_message_func:
self.tray_message_func("{0} - attaching {1}"
.format(vm.name, dev), msecs=3000)
qubesutils.block_attach(self.qvm_collection, vm,
self.free_devs[dev]['dev_obj'], mode=mode)
def detach_device(self, vm, dev_name):
frontend = self.attached_devs[dev_name]['attached_to']['frontend']
vm = self.attached_devs[dev_name]['attached_to']['vm']
if self.tray_message_func:
self.tray_message_func("{0} - detaching {1}".format(vm.name,
dev_name), msecs=3000)
qubesutils.block_detach(vm, frontend)
def check_if_serves_as_backend(self, vm):
serves_for = []
for d in self.attached_devs:
if self.attached_devs[d]['backend_name'] == vm.name:
serves_for.append((self.attached_devs[d]['dev'], self.attached_devs[d]['attached_to']['vm']))
if len(serves_for) > 0:
msg = "Qube <b>" + vm.name + "</b> attaches block devices to other qubes: "
msg += ', '.join(["<b>"+v.name+"</b>("+d+")" for (d,v) in serves_for ])
msg += ".<br><br> Shutting the qube down will dettach the devices from them."
QMessageBox.warning (None, "Warning!", msg)

View File

@ -21,7 +21,7 @@ import functools
import subprocess import subprocess
from . import utils from . import utils
from . import ui_bootfromdevice # pylint: disable=no-name-in-module from . import ui_bootfromdevice # pylint: disable=no-name-in-module
from PyQt5 import QtWidgets # pylint: disable=import-error from PyQt5 import QtWidgets, QtGui # pylint: disable=import-error
from qubesadmin import tools from qubesadmin import tools
from qubesadmin.tools import qvm_start from qubesadmin.tools import qvm_start
@ -47,6 +47,10 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog,
# warn user if the VM is currently running # warn user if the VM is currently running
self.__warn_if_running__() self.__warn_if_running__()
def setup_application(self):
self.qapp.setApplicationName(self.tr("Boot Qube From Device"))
self.qapp.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager"))
def reject(self): def reject(self):
self.done(0) self.done(0)
@ -148,8 +152,7 @@ def main(args=None):
args = parser.parse_args(args) args = parser.parse_args(args)
vm = args.domains.pop() vm = args.domains.pop()
utils.run_synchronous("Boot Qube From Device", utils.run_synchronous(functools.partial(VMBootFromDeviceWindow, vm))
functools.partial(VMBootFromDeviceWindow, vm))
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -27,6 +27,7 @@ from math import log
# pylint: disable=import-error # pylint: disable=import-error
from PyQt5.QtWidgets import QApplication, QMessageBox from PyQt5.QtWidgets import QApplication, QMessageBox
from PyQt5.QtCore import QCoreApplication
APPVIEWER_LOCK = "/var/run/qubes/appviewer.lock" APPVIEWER_LOCK = "/var/run/qubes/appviewer.lock"
CLIPBOARD_CONTENTS = "/var/run/qubes/qubes-clipboard.bin" CLIPBOARD_CONTENTS = "/var/run/qubes/qubes-clipboard.bin"
@ -43,14 +44,20 @@ def copy_text_to_qubes_clipboard(text):
try: try:
file = os.open(APPVIEWER_LOCK, os.O_RDWR | os.O_CREAT, 0o0666) file = os.open(APPVIEWER_LOCK, os.O_RDWR | os.O_CREAT, 0o0666)
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
QMessageBox.warning(None, "Warning!", QMessageBox.warning(
"Error while accessing Qubes clipboard!") None,
QCoreApplication.translate("Clipboard", "Warning!"),
QCoreApplication.translate(
"Clipboard", "Error while accessing Qubes clipboard!"))
else: else:
try: try:
fcntl.flock(file, fcntl.LOCK_EX) fcntl.flock(file, fcntl.LOCK_EX)
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
QMessageBox.warning(None, "Warning!", QMessageBox.warning(
"Error while locking Qubes clipboard!") None,
QCoreApplication.translate("Clipboard", "Warning!"),
QCoreApplication.translate(
"Clipboard", "Error while locking Qubes clipboard!"))
else: else:
try: try:
with open(CLIPBOARD_CONTENTS, "w") as contents: with open(CLIPBOARD_CONTENTS, "w") as contents:
@ -58,8 +65,11 @@ def copy_text_to_qubes_clipboard(text):
with open(CLIPBOARD_SOURCE, "w") as source: with open(CLIPBOARD_SOURCE, "w") as source:
source.write("dom0") source.write("dom0")
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
QMessageBox.warning(None, "Warning!", QMessageBox.warning(
"Error while writing to Qubes clipboard!") None,
QCoreApplication.translate("Clipboard", "Warning!"),
QCoreApplication.translate(
"Clipboard", "Error while writing to Qubes clipboard!"))
fcntl.flock(file, fcntl.LOCK_UN) fcntl.flock(file, fcntl.LOCK_UN)
os.close(file) os.close(file)
@ -71,11 +81,14 @@ def get_qubes_clipboard_formatted_size():
try: try:
file_size = os.path.getsize(CLIPBOARD_CONTENTS) file_size = os.path.getsize(CLIPBOARD_CONTENTS)
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
QMessageBox.warning(None, "Warning!", QMessageBox.warning(
"Error while accessing Qubes clipboard!") None,
QCoreApplication.translate("Clipboard", "Warning!"),
QCoreApplication.translate(
"Clipboard", "Error while accessing Qubes clipboard!"))
else: else:
formatted_bytes = '1 byte' if file_size == 1 \ formatted_bytes = QCoreApplication.translate(
else str(file_size) + ' bytes' "Clipboard", "%n byte(s)", "", file_size)
if file_size > 0: if file_size > 0:
magnitude = min(int(log(file_size)/log(2)*0.1), len(units)-1) magnitude = min(int(log(file_size)/log(2)*0.1), len(units)-1)
if magnitude > 0: if magnitude > 0:
@ -84,4 +97,4 @@ def get_qubes_clipboard_formatted_size():
units[magnitude]) units[magnitude])
return '%s' % formatted_bytes return '%s' % formatted_bytes
return '? bytes' return QCoreApplication.translate("Clipboard", '? bytes')

View File

@ -50,7 +50,7 @@ class RemoveVMThread(QubesThread):
try: try:
del self.vm.app.domains[self.vm.name] del self.vm.app.domains[self.vm.name]
except (exc.QubesException, KeyError) as ex: except (exc.QubesException, KeyError) as ex:
self.msg = ("Error removing qube!", str(ex)) self.msg = (self.tr("Error removing qube!"), str(ex))
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
@ -62,7 +62,8 @@ class CloneVMThread(QubesThread):
def run(self): def run(self):
try: try:
self.vm.app.clone_vm(self.vm, self.dst_name) self.vm.app.clone_vm(self.vm, self.dst_name)
self.msg = ("Sucess", "The qube was cloned sucessfully.") self.msg = (self.tr("Sucess"),
self.tr("The qube was cloned sucessfully."))
self.msg_is_success = True self.msg_is_success = True
except exc.QubesException as ex: except exc.QubesException as ex:
self.msg = ("Error while cloning qube!", str(ex)) self.msg = (self.tr("Error while cloning qube!"), str(ex))

View File

@ -21,6 +21,7 @@
# #
# #
import os
import sys import sys
import subprocess import subprocess
@ -204,7 +205,7 @@ class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg):
self.thread.start() self.thread.start()
self.progress = QtWidgets.QProgressDialog( self.progress = QtWidgets.QProgressDialog(
self.tr("Creating new qube <b>{}</b>...").format(name), "", 0, 0) self.tr("Creating new qube <b>{0}</b>...").format(name), "", 0, 0)
self.progress.setCancelButton(None) self.progress.setCancelButton(None)
self.progress.setModal(True) self.progress.setModal(True)
self.progress.show() self.progress.show()
@ -216,7 +217,7 @@ class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg):
QtWidgets.QMessageBox.warning( QtWidgets.QMessageBox.warning(
self, self,
self.tr("Error creating the qube!"), self.tr("Error creating the qube!"),
self.tr("ERROR: {}").format(self.thread.msg)) self.tr("ERROR: {0}").format(self.thread.msg))
self.done(0) self.done(0)
@ -266,9 +267,20 @@ def main(args=None):
args = parser.parse_args(args) args = parser.parse_args(args)
qtapp = QtWidgets.QApplication(sys.argv) qtapp = QtWidgets.QApplication(sys.argv)
translator = QtCore.QTranslator(qtapp)
locale = QtCore.QLocale.system().name()
i18n_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'i18n')
translator.load("qubesmanager_{!s}.qm".format(locale), i18n_dir)
qtapp.installTranslator(translator)
QtCore.QCoreApplication.installTranslator(translator)
qtapp.setOrganizationName('Invisible Things Lab') qtapp.setOrganizationName('Invisible Things Lab')
qtapp.setOrganizationDomain('https://www.qubes-os.org/') qtapp.setOrganizationDomain('https://www.qubes-os.org/')
qtapp.setApplicationName('Create qube') qtapp.setApplicationName(QtCore.QCoreApplication.translate(
"appname", 'Create qube'))
dialog = NewVmDlg(qtapp, args.app) dialog = NewVmDlg(qtapp, args.app)
dialog.exec_() dialog.exec_()

View File

@ -280,8 +280,7 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
return str(rule.proto) return str(rule.proto)
return "unknown" return "unknown"
@staticmethod def get_firewall_conf(self, vm):
def get_firewall_conf(vm):
conf = { conf = {
'allow': None, 'allow': None,
'expire': 0, 'expire': 0,
@ -296,14 +295,15 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
last_rule = next(reversed_rules, None) last_rule = next(reversed_rules, None)
if last_rule is None: if last_rule is None:
raise FirewallModifiedOutsideError('At least one rule must exist.') raise FirewallModifiedOutsideError(
self.tr('At least one rule must exist.'))
if last_rule == qubesadmin.firewall.Rule('action=accept') \ if last_rule == qubesadmin.firewall.Rule('action=accept') \
or last_rule == qubesadmin.firewall.Rule('action=drop'): or last_rule == qubesadmin.firewall.Rule('action=drop'):
common_action = last_rule.action common_action = last_rule.action
else: else:
raise FirewallModifiedOutsideError('Last rule must be either ' raise FirewallModifiedOutsideError(
'drop all or accept all.') self.tr('Last rule must be either drop all or accept all.'))
dns_rule = qubesadmin.firewall.Rule(None, dns_rule = qubesadmin.firewall.Rule(None,
action='accept', specialtarget='dns') action='accept', specialtarget='dns')
@ -319,29 +319,31 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
continue continue
if rule.specialtarget is not None or rule.icmptype is not None: if rule.specialtarget is not None or rule.icmptype is not None:
raise FirewallModifiedOutsideError("Rule type unknown!") raise FirewallModifiedOutsideError(
self.tr("Rule type unknown!"))
if (rule.dsthost is not None or rule.proto is not None) \ if (rule.dsthost is not None or rule.proto is not None) \
and rule.expire is None: and rule.expire is None:
if rule.action == 'accept': if rule.action == 'accept':
conf['rules'].insert(0, rule) conf['rules'].insert(0, rule)
continue continue
raise FirewallModifiedOutsideError('No blacklist support.') raise FirewallModifiedOutsideError(
self.tr('No blacklist support.'))
if rule.expire is not None and rule.dsthost is None \ if rule.expire is not None and rule.dsthost is None \
and rule.proto is None: and rule.proto is None:
conf['expire'] = int(str(rule.expire)) conf['expire'] = int(str(rule.expire))
continue continue
raise FirewallModifiedOutsideError('it does not add up.') raise FirewallModifiedOutsideError(self.tr('it does not add up.'))
conf['allow'] = (common_action == 'accept') conf['allow'] = (common_action == 'accept')
if not allow_icmp and not conf['allow']: if not allow_icmp and not conf['allow']:
raise FirewallModifiedOutsideError('ICMP must be allowed.') raise FirewallModifiedOutsideError(self.tr('ICMP must be allowed.'))
if not allow_dns and not conf['allow']: if not allow_dns and not conf['allow']:
raise FirewallModifiedOutsideError('DNS must be allowed') raise FirewallModifiedOutsideError(self.tr('DNS must be allowed'))
return conf return conf

View File

@ -21,7 +21,7 @@
# #
import subprocess import subprocess
from PyQt5 import QtWidgets # pylint: disable=import-error from PyQt5 import QtWidgets, QtCore, QtGui # pylint: disable=import-error
from qubesadmin.utils import parse_size from qubesadmin.utils import parse_size
@ -42,13 +42,19 @@ def _run_qrexec_repo(service, arg=''):
check=False check=False
) )
if p.stderr: if p.stderr:
raise RuntimeError('qrexec call stderr was not empty', raise RuntimeError(
QtCore.QCoreApplication.translate(
"GlobalSettings", 'qrexec call stderr was not empty'),
{'stderr': p.stderr.decode('utf-8')}) {'stderr': p.stderr.decode('utf-8')})
if p.returncode != 0: if p.returncode != 0:
raise RuntimeError('qrexec call exited with non-zero return code', raise RuntimeError(
QtCore.QCoreApplication.translate(
"GlobalSettings",
'qrexec call exited with non-zero return code'),
{'returncode': p.returncode}) {'returncode': p.returncode})
return p.stdout.decode('utf-8') return p.stdout.decode('utf-8')
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings, class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
QtWidgets.QDialog): QtWidgets.QDialog):
@ -69,6 +75,10 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
self.__init_mem_defaults__() self.__init_mem_defaults__()
self.__init_updates__() self.__init_updates__()
def setup_application(self):
self.qt_app.setApplicationName(self.tr("Qubes Global Settings"))
self.qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager"))
def __init_system_defaults__(self): def __init_system_defaults__(self):
# set up updatevm choice # set up updatevm choice
self.update_vm_vmlist, self.update_vm_idx = utils.prepare_vm_choice( self.update_vm_vmlist, self.update_vm_idx = utils.prepare_vm_choice(
@ -272,15 +282,16 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
elif repos['qubes-dom0-current']['enabled']: elif repos['qubes-dom0-current']['enabled']:
self.dom0_updates_repo.setCurrentIndex(0) self.dom0_updates_repo.setCurrentIndex(0)
else: else:
raise Exception('Cannot detect enabled dom0 update repositories') raise Exception(
self.tr('Cannot detect enabled dom0 update repositories'))
if repos['qubes-templates-itl-testing']['enabled']: if repos['qubes-templates-itl-testing']['enabled']:
self.itl_tmpl_updates_repo.setCurrentIndex(1) self.itl_tmpl_updates_repo.setCurrentIndex(1)
elif repos['qubes-templates-itl']['enabled']: elif repos['qubes-templates-itl']['enabled']:
self.itl_tmpl_updates_repo.setCurrentIndex(0) self.itl_tmpl_updates_repo.setCurrentIndex(0)
else: else:
raise Exception('Cannot detect enabled ITL template update ' raise Exception(self.tr('Cannot detect enabled ITL template update '
'repositories') 'repositories'))
if repos['qubes-templates-community-testing']['enabled']: if repos['qubes-templates-community-testing']['enabled']:
self.comm_tmpl_updates_repo.setCurrentIndex(2) self.comm_tmpl_updates_repo.setCurrentIndex(2)
@ -335,7 +346,8 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
result = _run_qrexec_repo('qubes.repos.' + action, name) result = _run_qrexec_repo('qubes.repos.' + action, name)
if result != 'ok\n': if result != 'ok\n':
raise RuntimeError( raise RuntimeError(
'qrexec call stdout did not contain "ok" as expected', self.tr('qrexec call stdout did not contain "ok"'
' as expected'),
{'stdout': result}) {'stdout': result})
except RuntimeError as ex: except RuntimeError as ex:
msg = '{desc}; {args}'.format(desc=ex.args[0], args=', '.join( msg = '{desc}; {args}'.format(desc=ex.args[0], args=', '.join(
@ -400,7 +412,7 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
def main(): def main():
utils.run_synchronous("Qubes Global Settings", GlobalSettingsWindow) utils.run_synchronous(GlobalSettingsWindow)
if __name__ == "__main__": if __name__ == "__main__":

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -159,6 +159,7 @@ class VmRowInTable:
if not event or event.endswith(':netvm'): if not event or event.endswith(':netvm'):
self.netvm_widget.update() self.netvm_widget.update()
if not event or event.endswith(':internal'): if not event or event.endswith(':internal'):
# this is a feature, not a property; TODO: fix event handling
self.internal_widget.update() self.internal_widget.update()
if not event or event.endswith(':ip'): if not event or event.endswith(':ip'):
self.ip_widget.update() self.ip_widget.update()
@ -298,18 +299,18 @@ class UpdateVMThread(common_threads.QubesThread):
if stdout == b'changed=yes\n': if stdout == b'changed=yes\n':
subprocess.call( subprocess.call(
['notify-send', '-i', 'dialog-information', ['notify-send', '-i', 'dialog-information',
'Debian DSA-4371 fix installed in {}'.format( self.tr('Debian DSA-4371 fix installed in {}').format(
self.vm.name)]) self.vm.name)])
elif stdout == b'changed=no\n': elif stdout == b'changed=no\n':
pass pass
else: else:
raise exc.QubesException( raise exc.QubesException(
"Failed to apply DSA-4371 fix: {}".format( self.tr("Failed to apply DSA-4371 fix: {}").format(
stderr.decode('ascii'))) stderr.decode('ascii')))
self.vm.run_service("qubes.InstallUpdatesGUI", self.vm.run_service("qubes.InstallUpdatesGUI",
user="root", wait=False) user="root", wait=False)
except (ChildProcessError, exc.QubesException) as ex: except (ChildProcessError, exc.QubesException) as ex:
self.msg = ("Error on qube update!", str(ex)) self.msg = (self.tr("Error on qube update!"), str(ex))
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
@ -322,7 +323,7 @@ class RunCommandThread(common_threads.QubesThread):
try: try:
self.vm.run(self.command_to_run) self.vm.run(self.command_to_run)
except (ChildProcessError, exc.QubesException) as ex: except (ChildProcessError, exc.QubesException) as ex:
self.msg = ("Error while running command!", str(ex)) self.msg = (self.tr("Error while running command!"), str(ex))
class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow): class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
@ -519,6 +520,11 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
# correctly initialized # correctly initialized
self.table.selectRow(0) self.table.selectRow(0)
def setup_application(self):
self.qt_app.setApplicationName(self.tr("Qube Manager"))
self.qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager"))
def keyPressEvent(self, event): # pylint: disable=invalid-name def keyPressEvent(self, event): # pylint: disable=invalid-name
if event.key() == QtCore.Qt.Key_Escape: if event.key() == QtCore.Qt.Key_Escape:
self.searchbox.clear() self.searchbox.clear()
@ -536,18 +542,18 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
if thread.msg_is_success: if thread.msg_is_success:
QtWidgets.QMessageBox.information( QtWidgets.QMessageBox.information(
self, self,
self.tr(title), title,
self.tr(msg)) msg)
else: else:
QtWidgets.QMessageBox.warning( QtWidgets.QMessageBox.warning(
self, self,
self.tr(title), title,
self.tr(msg)) msg)
self.threads_list.remove(thread) self.threads_list.remove(thread)
return return
raise RuntimeError('No finished thread found') raise RuntimeError(self.tr('No finished thread found'))
def closeEvent(self, event): def closeEvent(self, event):
# pylint: disable=invalid-name # pylint: disable=invalid-name
@ -888,7 +894,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
"Cloning Qube..."), "", 0, 0) "Cloning Qube..."), "", 0, 0)
self.progress.setCancelButton(None) self.progress.setCancelButton(None)
self.progress.setModal(True) self.progress.setModal(True)
self.progress.setWindowTitle("Cloning qube...") self.progress.setWindowTitle(self.tr("Cloning qube..."))
self.progress.show() self.progress.show()
thread = common_threads.CloneVMThread(vm, clone_name) thread = common_threads.CloneVMThread(vm, clone_name)
@ -1299,10 +1305,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
def main(): def main():
manager_utils.run_asynchronous( manager_utils.run_asynchronous(VmManagerWindow)
"Qube Manager",
"qubes-manager",
VmManagerWindow)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -20,7 +20,7 @@
# #
# #
from PyQt5 import QtCore, QtWidgets # pylint: disable=import-error from PyQt5 import QtCore, QtWidgets, QtGui # pylint: disable=import-error
import os import os
import os.path import os.path
import logging import logging
@ -110,6 +110,10 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtWidgets.QWizard):
backup_utils.fill_appvms_list(self) backup_utils.fill_appvms_list(self)
def setup_application(self):
self.qt_app.setApplicationName(self.tr("Qubes Restore VMs"))
self.qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager"))
@QtCore.pyqtSlot(name='on_select_path_button_clicked') @QtCore.pyqtSlot(name='on_select_path_button_clicked')
def select_path_button_clicked(self): def select_path_button_clicked(self):
backup_utils.select_path_button_clicked(self, True) backup_utils.select_path_button_clicked(self, True)
@ -290,7 +294,7 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtWidgets.QWizard):
def main(): def main():
utils.run_synchronous("Qubes Restore VMs", RestoreVMsWindow) utils.run_synchronous(RestoreVMsWindow)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -79,9 +79,9 @@ class RenameVMThread(common_threads.QubesThread):
self.vm.name) + list_text) self.vm.name) + list_text)
except qubesadmin.exc.QubesException as ex: except qubesadmin.exc.QubesException as ex:
self.msg = ("Rename error!", str(ex)) self.msg = (self.tr("Rename error!"), str(ex))
except Exception as ex: # pylint: disable=broad-except except Exception as ex: # pylint: disable=broad-except
self.msg = ("Rename error!", repr(ex)) self.msg = (self.tr("Rename error!"), repr(ex))
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
@ -112,7 +112,7 @@ class RefreshAppsVMThread(common_threads.QubesThread):
if not_running: if not_running:
vm.shutdown() vm.shutdown()
except Exception as ex: # pylint: disable=broad-except except Exception as ex: # pylint: disable=broad-except
self.msg = ("Refresh failed!", str(ex)) self.msg = (self.tr("Refresh failed!"), str(ex))
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
@ -222,6 +222,10 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.refresh_apps_button.clicked.connect( self.refresh_apps_button.clicked.connect(
self.refresh_apps_button_pressed) self.refresh_apps_button_pressed)
def setup_application(self):
self.qt_app.setApplicationName(self.tr("Qube Settings"))
self.qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager"))
def clear_threads(self): def clear_threads(self):
for thread in self.threads_list: for thread in self.threads_list:
if thread.isFinished(): if thread.isFinished():
@ -233,8 +237,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
(title, msg) = thread.msg (title, msg) = thread.msg
QtWidgets.QMessageBox.warning( QtWidgets.QMessageBox.warning(
self, self,
self.tr(title), title,
self.tr(msg)) msg)
self.threads_list.remove(thread) self.threads_list.remove(thread)
@ -243,7 +247,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
return return
raise RuntimeError('No finished thread found') raise RuntimeError(self.tr('No finished thread found'))
def keyPressEvent(self, event): # pylint: disable=invalid-name def keyPressEvent(self, event): # pylint: disable=invalid-name
if event.key() == QtCore.Qt.Key_Enter \ if event.key() == QtCore.Qt.Key_Enter \
@ -278,16 +282,16 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
try: try:
ret_tmp = self.__apply_basic_tab__() ret_tmp = self.__apply_basic_tab__()
if ret_tmp: if ret_tmp:
ret += ["Basic tab:"] + ret_tmp ret += [self.tr("Basic tab:")] + ret_tmp
ret_tmp = self.__apply_advanced_tab__() ret_tmp = self.__apply_advanced_tab__()
if ret_tmp: if ret_tmp:
ret += ["Advanced tab:"] + ret_tmp ret += [self.tr("Advanced tab:")] + ret_tmp
ret_tmp = self.__apply_devices_tab__() ret_tmp = self.__apply_devices_tab__()
if ret_tmp: if ret_tmp:
ret += ["Devices tab:"] + ret_tmp ret += [self.tr("Devices tab:")] + ret_tmp
ret_tmp = self.__apply_services_tab__() ret_tmp = self.__apply_services_tab__()
if ret_tmp: if ret_tmp:
ret += ["Sevices tab:"] + ret_tmp ret += [self.tr("Sevices tab:")] + ret_tmp
except qubesadmin.exc.QubesException as qex: except qubesadmin.exc.QubesException as qex:
ret.append(self.tr('Error while saving changes: ') + str(qex)) ret.append(self.tr('Error while saving changes: ') + str(qex))
except Exception as ex: # pylint: disable=broad-except except Exception as ex: # pylint: disable=broad-except
@ -587,8 +591,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
thread.finished.connect(self.clear_threads) thread.finished.connect(self.clear_threads)
self.progress = QtWidgets.QProgressDialog( self.progress = QtWidgets.QProgressDialog(
self.tr( self.tr("Renaming Qube..."), "", 0, 0)
"Renaming Qube..."), "", 0, 0)
self.progress.setCancelButton(None) self.progress.setCancelButton(None)
self.progress.setModal(True) self.progress.setModal(True)
self.thread_closes = True self.thread_closes = True
@ -644,8 +647,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
self.threads_list.append(thread) self.threads_list.append(thread)
self.progress = QtWidgets.QProgressDialog( self.progress = QtWidgets.QProgressDialog(
self.tr( self.tr("Cloning Qube..."), "", 0, 0)
"Cloning Qube..."), "", 0, 0)
self.progress.setCancelButton(None) self.progress.setCancelButton(None)
self.progress.setModal(True) self.progress.setModal(True)
self.thread_closes = True self.thread_closes = True
@ -727,9 +729,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
domains_using = [vm.name for vm in self.vm.connected_vms] domains_using = [vm.name for vm in self.vm.connected_vms]
if domains_using: if domains_using:
self.provides_network_checkbox.setEnabled(False) self.provides_network_checkbox.setEnabled(False)
self.provides_network_checkbox.setToolTip( self.provides_network_checkbox.setToolTip(self.tr(
"Cannot change this setting while this qube is used as a " "Cannot change this setting while this qube is used as a "
"NetVM by the following qubes:\n" + "NetVM by the following qubes:\n") +
"\n".join(domains_using)) "\n".join(domains_using))
def enable_seamless(self): def enable_seamless(self):
@ -1218,8 +1220,7 @@ def main(args=None):
args = parser.parse_args(args) args = parser.parse_args(args)
vm = args.domains.pop() vm = args.domains.pop()
utils.run_synchronous("Qube Settings", utils.run_synchronous(functools.partial(VMSettingsWindow, vm, args.tab))
functools.partial(VMSettingsWindow, vm, args.tab))
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -295,15 +295,16 @@ class VMPropertyItem(QtWidgets.QTableWidgetItem):
if self.empty_function(val): if self.empty_function(val):
text = "" text = ""
elif val is None: elif val is None:
text = "n/a" text = QtCore.QCoreApplication.translate("QubeManager", "n/a")
elif val is True: elif val is True:
text = "Yes" text = QtCore.QCoreApplication.translate("QubeManager", "Yes")
else: else:
text = str(val) text = str(val)
if self.check_default and hasattr(self.vm, self.property_name) and \ if self.check_default and hasattr(self.vm, self.property_name) and \
self.vm.property_is_default(self.property_name): self.vm.property_is_default(self.property_name):
text = 'default (' + text + ')' text = QtCore.QCoreApplication.translate(
"QubeManager", 'default ({})').format(text)
self.setText(text) self.setText(text)
def __lt__(self, other): def __lt__(self, other):
@ -338,7 +339,8 @@ class VmInternalItem(VMPropertyItem):
def update(self): def update(self):
internal = self.vm.features.get('internal', False) internal = self.vm.features.get('internal', False)
self.setText("Yes" if internal else "") self.setText(QtCore.QCoreApplication.translate(
"QubeManager", "Yes") if internal else "")
# features man qvm-features # features man qvm-features
@ -416,19 +418,28 @@ class VmUpdateInfoWidget(QtWidgets.QWidget):
self.value = state self.value = state
self.table_item.set_value(state) self.table_item.set_value(state)
if state == "update": if state == "update":
label_text = "<font color=\"#CCCC00\">Check updates</font>" label_text = "<font color=\"#CCCC00\">{}</font>".format(
QtCore.QCoreApplication.translate("QubeManager",
"Check updates"))
icon_path = ":/update-recommended.png" icon_path = ":/update-recommended.png"
tooltip_text = self.tr("Updates pending!") tooltip_text = QtCore.QCoreApplication.translate("QubeManager",
"Updates pending!")
elif state == "outdated": elif state == "outdated":
label_text = "<font color=\"red\">Qube outdated</font>" label_text = "<font color=\"red\">{}</font>".format(
QtCore.QCoreApplication.translate("QubeManager",
"Qube outdated"))
icon_path = ":/outdated.png" icon_path = ":/outdated.png"
tooltip_text = self.tr( tooltip_text = QtCore.QCoreApplication.translate(
"QubeManager",
"The qube must be restarted for its filesystem to reflect the " "The qube must be restarted for its filesystem to reflect the "
"template's recent committed changes.") "template's recent committed changes.")
elif state == "to-be-outdated": elif state == "to-be-outdated":
label_text = "<font color=\"#800000\">Template running</font>" label_text = "<font color=\"#800000\">{}</font>".format(
QtCore.QCoreApplication.translate("QubeManager",
"Template running"))
icon_path = ":/to-be-outdated.png" icon_path = ":/to-be-outdated.png"
tooltip_text = self.tr( tooltip_text = QtCore.QCoreApplication.translate(
"QubeManager",
"The Template must be stopped before changes from its " "The Template must be stopped before changes from its "
"current session can be picked up by this qube.") "current session can be picked up by this qube.")
else: else:
@ -466,7 +477,8 @@ class VmSizeOnDiskItem(QtWidgets.QTableWidgetItem):
def update(self): def update(self):
if self.vm.qid == 0: if self.vm.qid == 0:
self.setText("n/a") self.setText(QtCore.QCoreApplication.translate("QubeManager",
"n/a"))
else: else:
self.value = 10 self.value = 10
self.value = round(self.vm.get_disk_utilization()/(1024*1024), 2) self.value = round(self.vm.get_disk_utilization()/(1024*1024), 2)

View File

@ -62,11 +62,15 @@ class TemplateManagerWindow(
self.vm_list.show() self.vm_list.show()
def setup_application(self):
self.qt_app.setApplicationName(self.tr("Template Manager"))
self.qt_app.setWindowIcon(QtGui.QIcon.fromTheme("qubes-manager"))
def prepare_lists(self): def prepare_lists(self):
self.templates = [vm.name for vm in self.qubes_app.domains self.templates = [vm.name for vm in self.qubes_app.domains
if vm.klass == 'TemplateVM'] if vm.klass == 'TemplateVM']
self.change_all_combobox.addItem('(select template)') self.change_all_combobox.addItem(self.tr('(select template)'))
for template in self.templates: for template in self.templates:
self.change_all_combobox.addItem(template) self.change_all_combobox.addItem(template)
@ -84,7 +88,8 @@ class TemplateManagerWindow(
self.rows_in_table[vm.name] = row self.rows_in_table[vm.name] = row
row_count += 1 row_count += 1
self.vm_list.setHorizontalHeaderLabels(['', 'Qube', 'Current', 'New']) self.vm_list.setHorizontalHeaderLabels(
['', self.tr('Qube'), self.tr('Current'), self.tr('New')])
self.vm_list.resizeColumnsToContents() self.vm_list.resizeColumnsToContents()
def initialize_table_events(self): def initialize_table_events(self):
@ -258,7 +263,8 @@ class StatusItem(QtWidgets.QTableWidgetItem):
if self.state: if self.state:
self.setIcon(QtGui.QIcon.fromTheme('dialog-warning')) self.setIcon(QtGui.QIcon.fromTheme('dialog-warning'))
self.setToolTip("Cannot change template on a running VM.") self.setToolTip(QtCore.QCoreApplication.translate(
"template-manager", "Cannot change template on a running VM."))
else: else:
self.setIcon(QtGui.QIcon()) self.setIcon(QtGui.QIcon())
self.setToolTip("") self.setToolTip("")
@ -331,7 +337,9 @@ class VMRow:
self.current_item) self.current_item)
# new template # new template
self.dummy_new_item = QtWidgets.QTableWidgetItem("qube is running") self.dummy_new_item = QtWidgets.QTableWidgetItem(
QtCore.QCoreApplication.translate("TemplateManager",
"qube is running"))
self.new_item = NewTemplateItem(self.vm, templates, table_widget) self.new_item = NewTemplateItem(self.vm, templates, table_widget)
table_widget.setItem(row_no, columns.index('New template'), table_widget.setItem(row_no, columns.index('New template'),
@ -378,9 +386,7 @@ class VMRow:
def main(): def main():
utils.run_asynchronous("Template Manager", utils.run_asynchronous(TemplateManagerWindow)
"qubes-manager",
TemplateManagerWindow)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -30,7 +30,7 @@ import sys
import quamash import quamash
from qubesadmin import events from qubesadmin import events
from PyQt5 import QtWidgets # pylint: disable=import-error from PyQt5 import QtWidgets, QtCore # pylint: disable=import-error
from PyQt5.QtGui import QIcon # pylint: disable=import-error from PyQt5.QtGui import QIcon # pylint: disable=import-error
@ -96,10 +96,11 @@ def prepare_choice(widget, holder, propname, choice, default,
default_string = str(default) if default is not None else 'none' default_string = str(default) if default is not None else 'none'
if transform is not None: if transform is not None:
default_string = transform(default_string) default_string = transform(default_string)
text = 'default ({})'.format(default_string) text = QtCore.QCoreApplication.translate(
"ManagerUtils", 'default ({})').format(default_string)
# N+1: explicit None # N+1: explicit None
elif item is None: elif item is None:
text = '(none)' text = QtCore.QCoreApplication.translate("ManagerUtils", '(none)')
# 1..N: choices # 1..N: choices
else: else:
text = str(item) text = str(item)
@ -107,7 +108,8 @@ def prepare_choice(widget, holder, propname, choice, default,
text = transform(text) text = transform(text)
if item == oldvalue: if item == oldvalue:
text += ' (current)' text += QtCore.QCoreApplication.translate(
"ManagerUtils", ' (current)')
idx = i idx = i
widget.insertItem(i, text) widget.insertItem(i, text)
@ -210,7 +212,8 @@ def get_path_from_vm(vm, service_name):
assert '../' not in untrusted_path assert '../' not in untrusted_path
assert '\0' not in untrusted_path assert '\0' not in untrusted_path
return untrusted_path.strip() return untrusted_path.strip()
raise ValueError('Unexpected characters in path.') raise ValueError(QtCore.QCoreApplication.translate(
"ManagerUtils", 'Unexpected characters in path.'))
def format_dependencies_list(dependencies): def format_dependencies_list(dependencies):
@ -220,9 +223,11 @@ def format_dependencies_list(dependencies):
list_text = "" list_text = ""
for (holder, prop) in dependencies: for (holder, prop) in dependencies:
if holder is None: if holder is None:
list_text += "- Global property <b>{}</b> <br>".format(prop) list_text += QtCore.QCoreApplication.translate(
"ManagerUtils", "- Global property <b>{}</b> <br>").format(prop)
else: else:
list_text += "- <b>{}</b> for qube <b>{}</b> <br>".format( list_text += QtCore.QCoreApplication.translate(
"ManagerUtils", "- <b>{0}</b> for qube <b>{1}</b> <br>").format(
prop, holder.name) prop, holder.name)
return list_text return list_text
@ -256,22 +261,31 @@ def handle_exception(exc_type, exc_value, exc_traceback):
msg_box = QtWidgets.QMessageBox() msg_box = QtWidgets.QMessageBox()
msg_box.setDetailedText(strace) msg_box.setDetailedText(strace)
msg_box.setIcon(QtWidgets.QMessageBox.Critical) msg_box.setIcon(QtWidgets.QMessageBox.Critical)
msg_box.setWindowTitle("Houston, we have a problem...") msg_box.setWindowTitle(QtCore.QCoreApplication.translate(
msg_box.setText("Whoops. A critical error has occured. " "ManagerUtils", "Houston, we have a problem..."))
msg_box.setText(QtCore.QCoreApplication.translate(
"ManagerUtils", "Whoops. A critical error has occured. "
"This is most likely a bug in Qubes Manager.<br><br>" "This is most likely a bug in Qubes Manager.<br><br>"
"<b><i>%s</i></b>" % error + "<b><i>{0}</i></b><br/>at line <b>{1}</b><br/>of file "
"<br/>at line <b>%d</b><br/>of file %s.<br/><br/>" "{2}.<br/><br/>").format(error, line, filename))
% (line, filename))
msg_box.exec_() msg_box.exec_()
def run_asynchronous(app_name, icon_name, window_class): def run_asynchronous(window_class):
qt_app = QtWidgets.QApplication(sys.argv) qt_app = QtWidgets.QApplication(sys.argv)
translator = QtCore.QTranslator(qt_app)
locale = QtCore.QLocale.system().name()
i18n_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'i18n')
translator.load("qubesmanager_{!s}.qm".format(locale), i18n_dir)
qt_app.installTranslator(translator)
QtCore.QCoreApplication.installTranslator(translator)
qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationName("The Qubes Project")
qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.setOrganizationDomain("http://qubes-os.org")
qt_app.setApplicationName(app_name)
qt_app.setWindowIcon(QIcon.fromTheme(icon_name))
qt_app.lastWindowClosed.connect(loop_shutdown) qt_app.lastWindowClosed.connect(loop_shutdown)
qubes_app = qubesadmin.Qubes() qubes_app = qubesadmin.Qubes()
@ -281,6 +295,10 @@ def run_asynchronous(app_name, icon_name, window_class):
dispatcher = events.EventsDispatcher(qubes_app) dispatcher = events.EventsDispatcher(qubes_app)
window = window_class(qt_app, qubes_app, dispatcher) window = window_class(qt_app, qubes_app, dispatcher)
if hasattr(window, "setup_application"):
window.setup_application()
window.show() window.show()
try: try:
@ -294,11 +312,20 @@ def run_asynchronous(app_name, icon_name, window_class):
handle_exception(exc_type, exc_value, exc_traceback) handle_exception(exc_type, exc_value, exc_traceback)
def run_synchronous(app_name, window_class): def run_synchronous(window_class):
qt_app = QtWidgets.QApplication(sys.argv) qt_app = QtWidgets.QApplication(sys.argv)
translator = QtCore.QTranslator(qt_app)
locale = QtCore.QLocale.system().name()
i18n_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'i18n')
translator.load("qubesmanager_{!s}.qm".format(locale), i18n_dir)
qt_app.installTranslator(translator)
QtCore.QCoreApplication.installTranslator(translator)
qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationName("The Qubes Project")
qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.setOrganizationDomain("http://qubes-os.org")
qt_app.setApplicationName(app_name)
sys.excepthook = handle_exception sys.excepthook = handle_exception
@ -306,6 +333,9 @@ def run_synchronous(app_name, window_class):
window = window_class(qt_app, qubes_app) window = window_class(qt_app, qubes_app)
if hasattr(window, "setup_application"):
window.setup_application()
window.show() window.show()
qt_app.exec_() qt_app.exec_()

View File

@ -90,7 +90,6 @@ rm -rf $RPM_BUILD_ROOT
%{python3_sitelib}/qubesmanager/__pycache__ %{python3_sitelib}/qubesmanager/__pycache__
%{python3_sitelib}/qubesmanager/__init__.py %{python3_sitelib}/qubesmanager/__init__.py
%{python3_sitelib}/qubesmanager/clipboard.py %{python3_sitelib}/qubesmanager/clipboard.py
%{python3_sitelib}/qubesmanager/block.py
%{python3_sitelib}/qubesmanager/table_widgets.py %{python3_sitelib}/qubesmanager/table_widgets.py
%{python3_sitelib}/qubesmanager/appmenu_select.py %{python3_sitelib}/qubesmanager/appmenu_select.py
%{python3_sitelib}/qubesmanager/backup.py %{python3_sitelib}/qubesmanager/backup.py