mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00
Refactor Common into GlobalCommon (for the whole app) and Common (for a window), and allow multiple dangerzone windows at once in macOS
This commit is contained in:
parent
cb38473573
commit
4c9bbfe217
9 changed files with 562 additions and 523 deletions
|
@ -6,7 +6,7 @@ import platform
|
||||||
import click
|
import click
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from .common import Common
|
from .global_common import GlobalCommon
|
||||||
from .main_window import MainWindow
|
from .main_window import MainWindow
|
||||||
from .docker_installer import (
|
from .docker_installer import (
|
||||||
is_docker_installed,
|
is_docker_installed,
|
||||||
|
@ -45,31 +45,36 @@ def main(filename):
|
||||||
app = Application()
|
app = Application()
|
||||||
app.setQuitOnLastWindowClosed(False)
|
app.setQuitOnLastWindowClosed(False)
|
||||||
|
|
||||||
# Common object
|
# GlobalCommon object
|
||||||
common = Common(app)
|
global_common = GlobalCommon(app)
|
||||||
|
|
||||||
# If we're using Linux and docker, see if we need to add the user to the docker group
|
# If we're using Linux and docker, see if we need to add the user to the docker group
|
||||||
if platform.system() == "Linux" and common.container_runtime == "/usr/bin/docker":
|
if (
|
||||||
if not common.ensure_user_is_in_docker_group():
|
platform.system() == "Linux"
|
||||||
|
and global_common.container_runtime == "/usr/bin/docker"
|
||||||
|
):
|
||||||
|
if not global_common.ensure_user_is_in_docker_group():
|
||||||
print("Failed to add user to docker group")
|
print("Failed to add user to docker group")
|
||||||
return
|
return
|
||||||
|
|
||||||
# See if we need to install Docker...
|
# See if we need to install Docker...
|
||||||
if (platform.system() == "Darwin" or platform.system() == "Windows") and (
|
if (platform.system() == "Darwin" or platform.system() == "Windows") and (
|
||||||
not is_docker_installed(common) or not is_docker_ready(common)
|
not is_docker_installed(global_common) or not is_docker_ready(global_common)
|
||||||
):
|
):
|
||||||
print("Docker is either not installed or not running")
|
print("Docker is either not installed or not running")
|
||||||
docker_installer = DockerInstaller(common)
|
docker_installer = DockerInstaller(global_common)
|
||||||
docker_installer.start()
|
docker_installer.start()
|
||||||
return
|
return
|
||||||
|
|
||||||
windows = []
|
windows = []
|
||||||
doc_opened = False
|
|
||||||
|
|
||||||
# Open a document in a window
|
# Open a document in a window
|
||||||
def select_document(filename=None):
|
def select_document(filename=None):
|
||||||
print(f"select_document, filename={filename}")
|
if len(windows) == 1 and windows[0].common.document_filename == None:
|
||||||
new_window = MainWindow(common)
|
window = windows[0]
|
||||||
|
else:
|
||||||
|
window = MainWindow(global_common)
|
||||||
|
windows.append(window)
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
# Validate filename
|
# Validate filename
|
||||||
|
@ -82,33 +87,18 @@ def main(filename):
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
print("Permission denied")
|
print("Permission denied")
|
||||||
return False
|
return False
|
||||||
common.set_document_filename(filename)
|
window.common.document_filename = filename
|
||||||
new_window.doc_selection_widget.document_selected.emit()
|
window.doc_selection_widget.document_selected.emit()
|
||||||
|
|
||||||
windows.append(new_window)
|
|
||||||
doc_opened = True
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# If filename is passed as an argument, open it
|
# Open a new window if not filename is passed
|
||||||
if filename is None:
|
if filename is None:
|
||||||
if platform.system() == "Darwin":
|
select_document()
|
||||||
# Wait 0.5 seconds to see if we can an open document event, and if not open a new window without a document
|
|
||||||
def open_window_if_not_already_open():
|
|
||||||
if not doc_opened:
|
|
||||||
select_document()
|
|
||||||
|
|
||||||
timer = QtCore.QTimer()
|
|
||||||
timer.setSingleShot(True)
|
|
||||||
timer.setInterval(500)
|
|
||||||
timer.timeout.connect(open_window_if_not_already_open)
|
|
||||||
timer.start()
|
|
||||||
else:
|
|
||||||
# Open a new window without a document
|
|
||||||
select_document()
|
|
||||||
else:
|
else:
|
||||||
# Try opening a window with the docoument selected
|
# If filename is passed as an argument, open it
|
||||||
if not select_document(filename):
|
if not select_document(filename):
|
||||||
return False
|
return True
|
||||||
|
|
||||||
# If we get a file open event, open it
|
# If we get a file open event, open it
|
||||||
app.document_selected.connect(select_document)
|
app.document_selected.connect(select_document)
|
||||||
|
|
|
@ -1,35 +1,13 @@
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import inspect
|
|
||||||
import tempfile
|
|
||||||
import appdirs
|
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import tempfile
|
||||||
import shlex
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
||||||
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
import CoreServices
|
|
||||||
import LaunchServices
|
|
||||||
import plistlib
|
|
||||||
|
|
||||||
elif platform.system() == "Linux":
|
|
||||||
import grp
|
|
||||||
import getpass
|
|
||||||
from xdg.DesktopEntry import DesktopEntry
|
|
||||||
|
|
||||||
from .settings import Settings
|
|
||||||
|
|
||||||
|
|
||||||
class Common(object):
|
class Common(object):
|
||||||
"""
|
"""
|
||||||
The Common class is a singleton of shared functionality throughout the app
|
The Common class is a singleton of shared functionality throughout an open dangerzone window
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self):
|
||||||
# Qt app
|
|
||||||
self.app = app
|
|
||||||
|
|
||||||
# Temporary directory to store pixel data
|
# Temporary directory to store pixel data
|
||||||
# Note in macOS, temp dirs must be in /tmp (or a few other paths) for Docker to mount them
|
# Note in macOS, temp dirs must be in /tmp (or a few other paths) for Docker to mount them
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
|
@ -44,411 +22,6 @@ class Common(object):
|
||||||
f"Temporary directories created, dangerous={self.pixel_dir.name}, safe={self.safe_dir.name}"
|
f"Temporary directories created, dangerous={self.pixel_dir.name}, safe={self.safe_dir.name}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Name of input file
|
# Name of input and out files
|
||||||
self.document_filename = None
|
self.document_filename = None
|
||||||
|
|
||||||
# Name of output file
|
|
||||||
self.save_filename = None
|
self.save_filename = None
|
||||||
|
|
||||||
# Preload font
|
|
||||||
self.fixed_font = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)
|
|
||||||
|
|
||||||
# App data folder
|
|
||||||
self.appdata_path = appdirs.user_config_dir("dangerzone")
|
|
||||||
|
|
||||||
# Container runtime
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
self.container_runtime = "/usr/local/bin/docker"
|
|
||||||
elif platform.system() == "Windows":
|
|
||||||
self.container_runtime = (
|
|
||||||
"C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Linux
|
|
||||||
|
|
||||||
# If this is fedora-like, use podman
|
|
||||||
if os.path.exists("/usr/bin/dnf"):
|
|
||||||
self.container_runtime = "/usr/bin/podman"
|
|
||||||
# Otherwise, use docker
|
|
||||||
else:
|
|
||||||
self.container_runtime = "/usr/bin/docker"
|
|
||||||
|
|
||||||
# Preload list of PDF viewers on computer
|
|
||||||
self.pdf_viewers = self._find_pdf_viewers()
|
|
||||||
|
|
||||||
# Languages supported by tesseract
|
|
||||||
self.ocr_languages = {
|
|
||||||
"Afrikaans": "ar",
|
|
||||||
"Albanian": "sqi",
|
|
||||||
"Amharic": "amh",
|
|
||||||
"Arabic": "ara",
|
|
||||||
"Arabic script": "Arabic",
|
|
||||||
"Armenian": "hye",
|
|
||||||
"Armenian script": "Armenian",
|
|
||||||
"Assamese": "asm",
|
|
||||||
"Azerbaijani": "aze",
|
|
||||||
"Azerbaijani (Cyrillic)": "aze_cyrl",
|
|
||||||
"Basque": "eus",
|
|
||||||
"Belarusian": "bel",
|
|
||||||
"Bengali": "ben",
|
|
||||||
"Bengali script": "Bengali",
|
|
||||||
"Bosnian": "bos",
|
|
||||||
"Breton": "bre",
|
|
||||||
"Bulgarian": "bul",
|
|
||||||
"Burmese": "mya",
|
|
||||||
"Canadian Aboriginal script": "Canadian_Aboriginal",
|
|
||||||
"Catalan": "cat",
|
|
||||||
"Cebuano": "ceb",
|
|
||||||
"Cherokee": "chr",
|
|
||||||
"Cherokee script": "Cherokee",
|
|
||||||
"Chinese - Simplified": "chi_sim",
|
|
||||||
"Chinese - Simplified (vertical)": "chi_sim_vert",
|
|
||||||
"Chinese - Traditional": "chi_tra",
|
|
||||||
"Chinese - Traditional (vertical)": "chi_tra_vert",
|
|
||||||
"Corsican": "cos",
|
|
||||||
"Croatian": "hrv",
|
|
||||||
"Cyrillic script": "Cyrillic",
|
|
||||||
"Czech": "ces",
|
|
||||||
"Danish": "dan",
|
|
||||||
"Devanagari script": "Devanagari",
|
|
||||||
"Divehi": "div",
|
|
||||||
"Dutch": "nld",
|
|
||||||
"Dzongkha": "dzo",
|
|
||||||
"English": "eng",
|
|
||||||
"English, Middle (1100-1500)": "enm",
|
|
||||||
"Esperanto": "epo",
|
|
||||||
"Estonian": "est",
|
|
||||||
"Ethiopic script": "Ethiopic",
|
|
||||||
"Faroese": "fao",
|
|
||||||
"Filipino": "fil",
|
|
||||||
"Finnish": "fin",
|
|
||||||
"Fraktur script": "Fraktur",
|
|
||||||
"Frankish": "frk",
|
|
||||||
"French": "fra",
|
|
||||||
"French, Middle (ca.1400-1600)": "frm",
|
|
||||||
"Frisian (Western)": "fry",
|
|
||||||
"Gaelic (Scots)": "gla",
|
|
||||||
"Galician": "glg",
|
|
||||||
"Georgian": "kat",
|
|
||||||
"Georgian script": "Georgian",
|
|
||||||
"German": "deu",
|
|
||||||
"Greek": "ell",
|
|
||||||
"Greek script": "Greek",
|
|
||||||
"Gujarati": "guj",
|
|
||||||
"Gujarati script": "Gujarati",
|
|
||||||
"Gurmukhi script": "Gurmukhi",
|
|
||||||
"Hangul script": "Hangul",
|
|
||||||
"Hangul (vertical) script": "Hangul_vert",
|
|
||||||
"Han - Simplified script": "HanS",
|
|
||||||
"Han - Simplified (vertical) script": "HanS_vert",
|
|
||||||
"Han - Traditional script": "HanT",
|
|
||||||
"Han - Traditional (vertical) script": "HanT_vert",
|
|
||||||
"Hatian": "hat",
|
|
||||||
"Hebrew": "heb",
|
|
||||||
"Hebrew script": "Hebrew",
|
|
||||||
"Hindi": "hin",
|
|
||||||
"Hungarian": "hun",
|
|
||||||
"Icelandic": "isl",
|
|
||||||
"Indonesian": "ind",
|
|
||||||
"Inuktitut": "iku",
|
|
||||||
"Irish": "gle",
|
|
||||||
"Italian": "ita",
|
|
||||||
"Italian - Old": "ita_old",
|
|
||||||
"Japanese": "jpn",
|
|
||||||
"Japanese script": "Japanese",
|
|
||||||
"Japanese (vertical)": "jpn_vert",
|
|
||||||
"Japanese (vertical) script": "Japanese_vert",
|
|
||||||
"Javanese": "jav",
|
|
||||||
"Kannada": "kan",
|
|
||||||
"Kannada script": "Kannada",
|
|
||||||
"Kazakh": "kaz",
|
|
||||||
"Khmer": "khm",
|
|
||||||
"Khmer script": "Khmer",
|
|
||||||
"Korean": "kor",
|
|
||||||
"Korean (vertical)": "kor_vert",
|
|
||||||
"Kurdish (Arabic)": "kur_ara",
|
|
||||||
"Kyrgyz": "kir",
|
|
||||||
"Lao": "lao",
|
|
||||||
"Lao script": "Lao",
|
|
||||||
"Latin": "lat",
|
|
||||||
"Latin script": "Latin",
|
|
||||||
"Latvian": "lav",
|
|
||||||
"Lithuanian": "lit",
|
|
||||||
"Luxembourgish": "ltz",
|
|
||||||
"Macedonian": "mkd",
|
|
||||||
"Malayalam": "mal",
|
|
||||||
"Malayalam script": "Malayalam",
|
|
||||||
"Malay": "msa",
|
|
||||||
"Maltese": "mlt",
|
|
||||||
"Maori": "mri",
|
|
||||||
"Marathi": "mar",
|
|
||||||
"Mongolian": "mon",
|
|
||||||
"Myanmar script": "Myanmar",
|
|
||||||
"Nepali": "nep",
|
|
||||||
"Norwegian": "nor",
|
|
||||||
"Occitan (post 1500)": "oci",
|
|
||||||
"Old Georgian": "kat_old",
|
|
||||||
"Oriya (Odia) script": "Oriya",
|
|
||||||
"Oriya": "ori",
|
|
||||||
"Pashto": "pus",
|
|
||||||
"Persian": "fas",
|
|
||||||
"Polish": "pol",
|
|
||||||
"Portuguese": "por",
|
|
||||||
"Punjabi": "pan",
|
|
||||||
"Quechua": "que",
|
|
||||||
"Romanian": "ron",
|
|
||||||
"Russian": "rus",
|
|
||||||
"Sanskrit": "san",
|
|
||||||
"script and orientation": "osd",
|
|
||||||
"Serbian (Latin)": "srp_latn",
|
|
||||||
"Serbian": "srp",
|
|
||||||
"Sindhi": "snd",
|
|
||||||
"Sinhala script": "Sinhala",
|
|
||||||
"Sinhala": "sin",
|
|
||||||
"Slovakian": "slk",
|
|
||||||
"Slovenian": "slv",
|
|
||||||
"Spanish, Castilian - Old": "spa_old",
|
|
||||||
"Spanish": "spa",
|
|
||||||
"Sundanese": "sun",
|
|
||||||
"Swahili": "swa",
|
|
||||||
"Swedish": "swe",
|
|
||||||
"Syriac script": "Syriac",
|
|
||||||
"Syriac": "syr",
|
|
||||||
"Tajik": "tgk",
|
|
||||||
"Tamil script": "Tamil",
|
|
||||||
"Tamil": "tam",
|
|
||||||
"Tatar": "tat",
|
|
||||||
"Telugu script": "Telugu",
|
|
||||||
"Telugu": "tel",
|
|
||||||
"Thaana script": "Thaana",
|
|
||||||
"Thai script": "Thai",
|
|
||||||
"Thai": "tha",
|
|
||||||
"Tibetan script": "Tibetan",
|
|
||||||
"Tibetan Standard": "bod",
|
|
||||||
"Tigrinya": "tir",
|
|
||||||
"Tonga": "ton",
|
|
||||||
"Turkish": "tur",
|
|
||||||
"Ukrainian": "ukr",
|
|
||||||
"Urdu": "urd",
|
|
||||||
"Uyghur": "uig",
|
|
||||||
"Uzbek (Cyrillic)": "uzb_cyrl",
|
|
||||||
"Uzbek": "uzb",
|
|
||||||
"Vietnamese script": "Vietnamese",
|
|
||||||
"Vietnamese": "vie",
|
|
||||||
"Welsh": "cym",
|
|
||||||
"Yiddish": "yid",
|
|
||||||
"Yoruba": "yor",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Load settings
|
|
||||||
self.settings = Settings(self)
|
|
||||||
|
|
||||||
def set_document_filename(self, filename):
|
|
||||||
self.document_filename = filename
|
|
||||||
|
|
||||||
def get_resource_path(self, filename):
|
|
||||||
if getattr(sys, "dangerzone_dev", False):
|
|
||||||
# Look for resources directory relative to python file
|
|
||||||
prefix = os.path.join(
|
|
||||||
os.path.dirname(
|
|
||||||
os.path.dirname(
|
|
||||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
|
||||||
)
|
|
||||||
),
|
|
||||||
"share",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
prefix = os.path.join(
|
|
||||||
os.path.dirname(os.path.dirname(sys.executable)), "Resources/share"
|
|
||||||
)
|
|
||||||
elif platform.system() == "Linux":
|
|
||||||
prefix = os.path.join(sys.prefix, "share", "dangerzone")
|
|
||||||
else:
|
|
||||||
# Windows
|
|
||||||
prefix = os.path.join(os.path.dirname(sys.executable), "share")
|
|
||||||
|
|
||||||
resource_path = os.path.join(prefix, filename)
|
|
||||||
return resource_path
|
|
||||||
|
|
||||||
def get_window_icon(self):
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
path = self.get_resource_path("dangerzone.ico")
|
|
||||||
else:
|
|
||||||
path = self.get_resource_path("logo.png")
|
|
||||||
return QtGui.QIcon(path)
|
|
||||||
|
|
||||||
def open_pdf_viewer(self, filename):
|
|
||||||
if self.settings.get("open_app") in self.pdf_viewers:
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
# Get the PDF reader bundle command
|
|
||||||
bundle_identifier = self.pdf_viewers[self.settings.get("open_app")]
|
|
||||||
args = ["open", "-b", bundle_identifier, filename]
|
|
||||||
|
|
||||||
# Run
|
|
||||||
print(f"Executing: {' '.join(args)}")
|
|
||||||
subprocess.run(args)
|
|
||||||
|
|
||||||
elif platform.system() == "Linux":
|
|
||||||
# Get the PDF reader command
|
|
||||||
args = shlex.split(self.pdf_viewers[self.settings.get("open_app")])
|
|
||||||
# %f, %F, %u, and %U are filenames or URLS -- so replace with the file to open
|
|
||||||
for i in range(len(args)):
|
|
||||||
if (
|
|
||||||
args[i] == "%f"
|
|
||||||
or args[i] == "%F"
|
|
||||||
or args[i] == "%u"
|
|
||||||
or args[i] == "%U"
|
|
||||||
):
|
|
||||||
args[i] = filename
|
|
||||||
|
|
||||||
# Open as a background process
|
|
||||||
print(f"Executing: {' '.join(args)}")
|
|
||||||
subprocess.Popen(args)
|
|
||||||
|
|
||||||
def _find_pdf_viewers(self):
|
|
||||||
pdf_viewers = {}
|
|
||||||
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
# Get all installed apps that can open PDFs
|
|
||||||
bundle_identifiers = LaunchServices.LSCopyAllRoleHandlersForContentType(
|
|
||||||
"com.adobe.pdf", CoreServices.kLSRolesAll
|
|
||||||
)
|
|
||||||
for bundle_identifier in bundle_identifiers:
|
|
||||||
# Get the filesystem path of the app
|
|
||||||
res = LaunchServices.LSCopyApplicationURLsForBundleIdentifier(
|
|
||||||
bundle_identifier, None
|
|
||||||
)
|
|
||||||
if res[0] is None:
|
|
||||||
continue
|
|
||||||
app_url = res[0][0]
|
|
||||||
app_path = str(app_url.path())
|
|
||||||
|
|
||||||
# Load its plist file
|
|
||||||
plist_path = os.path.join(app_path, "Contents/Info.plist")
|
|
||||||
with open(plist_path, "rb") as f:
|
|
||||||
plist_data = f.read()
|
|
||||||
plist_dict = plistlib.loads(plist_data)
|
|
||||||
|
|
||||||
if plist_dict["CFBundleName"] != "Dangerzone":
|
|
||||||
pdf_viewers[plist_dict["CFBundleName"]] = bundle_identifier
|
|
||||||
|
|
||||||
elif platform.system() == "Linux":
|
|
||||||
# Find all .desktop files
|
|
||||||
for search_path in [
|
|
||||||
"/usr/share/applications",
|
|
||||||
"/usr/local/share/applications",
|
|
||||||
os.path.expanduser("~/.local/share/applications"),
|
|
||||||
]:
|
|
||||||
try:
|
|
||||||
for filename in os.listdir(search_path):
|
|
||||||
full_filename = os.path.join(search_path, filename)
|
|
||||||
if os.path.splitext(filename)[1] == ".desktop":
|
|
||||||
|
|
||||||
# See which ones can open PDFs
|
|
||||||
desktop_entry = DesktopEntry(full_filename)
|
|
||||||
if (
|
|
||||||
"application/pdf" in desktop_entry.getMimeTypes()
|
|
||||||
and desktop_entry.getName() != "dangerzone"
|
|
||||||
):
|
|
||||||
pdf_viewers[
|
|
||||||
desktop_entry.getName()
|
|
||||||
] = desktop_entry.getExec()
|
|
||||||
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return pdf_viewers
|
|
||||||
|
|
||||||
def ensure_user_is_in_docker_group(self):
|
|
||||||
try:
|
|
||||||
groupinfo = grp.getgrnam("docker")
|
|
||||||
except:
|
|
||||||
# Ignore if group is not found
|
|
||||||
return True
|
|
||||||
|
|
||||||
username = getpass.getuser()
|
|
||||||
if username not in groupinfo.gr_mem:
|
|
||||||
# User is not in docker group, so prompt about adding the user to the docker group
|
|
||||||
message = "<b>Dangerzone requires Docker.</b><br><br>Click Ok to add your user to the 'docker' group. You will have to type your login password."
|
|
||||||
if Alert(self, message).launch():
|
|
||||||
p = subprocess.run(
|
|
||||||
[
|
|
||||||
"/usr/bin/pkexec",
|
|
||||||
"/usr/sbin/usermod",
|
|
||||||
"-a",
|
|
||||||
"-G",
|
|
||||||
"docker",
|
|
||||||
username,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if p.returncode == 0:
|
|
||||||
message = "Great! Now you must log out of your computer and log back in, and then you can use Dangerzone."
|
|
||||||
Alert(self, message).launch()
|
|
||||||
else:
|
|
||||||
message = "Failed to add your user to the 'docker' group, quitting."
|
|
||||||
Alert(self, message).launch()
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_subprocess_startupinfo(self):
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
startupinfo = subprocess.STARTUPINFO()
|
|
||||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
||||||
return startupinfo
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class Alert(QtWidgets.QDialog):
|
|
||||||
def __init__(self, common, message):
|
|
||||||
super(Alert, self).__init__()
|
|
||||||
self.common = common
|
|
||||||
|
|
||||||
self.setWindowTitle("dangerzone")
|
|
||||||
self.setWindowIcon(self.common.get_window_icon())
|
|
||||||
self.setModal(True)
|
|
||||||
|
|
||||||
flags = (
|
|
||||||
QtCore.Qt.CustomizeWindowHint
|
|
||||||
| QtCore.Qt.WindowTitleHint
|
|
||||||
| QtCore.Qt.WindowSystemMenuHint
|
|
||||||
| QtCore.Qt.WindowCloseButtonHint
|
|
||||||
| QtCore.Qt.WindowStaysOnTopHint
|
|
||||||
)
|
|
||||||
self.setWindowFlags(flags)
|
|
||||||
|
|
||||||
logo = QtWidgets.QLabel()
|
|
||||||
logo.setPixmap(
|
|
||||||
QtGui.QPixmap.fromImage(
|
|
||||||
QtGui.QImage(self.common.get_resource_path("icon.png"))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
label = QtWidgets.QLabel()
|
|
||||||
label.setText(message)
|
|
||||||
label.setWordWrap(True)
|
|
||||||
|
|
||||||
message_layout = QtWidgets.QHBoxLayout()
|
|
||||||
message_layout.addWidget(logo)
|
|
||||||
message_layout.addWidget(label)
|
|
||||||
|
|
||||||
ok_button = QtWidgets.QPushButton("Ok")
|
|
||||||
ok_button.clicked.connect(self.accept)
|
|
||||||
cancel_button = QtWidgets.QPushButton("Cancel")
|
|
||||||
cancel_button.clicked.connect(self.reject)
|
|
||||||
|
|
||||||
buttons_layout = QtWidgets.QHBoxLayout()
|
|
||||||
buttons_layout.addStretch()
|
|
||||||
buttons_layout.addWidget(ok_button)
|
|
||||||
buttons_layout.addWidget(cancel_button)
|
|
||||||
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addLayout(message_layout)
|
|
||||||
layout.addLayout(buttons_layout)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def launch(self):
|
|
||||||
return self.exec_() == QtWidgets.QDialog.Accepted
|
|
||||||
|
|
|
@ -39,5 +39,5 @@ class DocSelectionWidget(QtWidgets.QWidget):
|
||||||
)
|
)
|
||||||
if filename[0] != "":
|
if filename[0] != "":
|
||||||
filename = filename[0]
|
filename = filename[0]
|
||||||
self.common.set_document_filename(filename)
|
self.common.document_filename = filename
|
||||||
self.document_selected.emit()
|
self.document_selected.emit()
|
||||||
|
|
|
@ -9,49 +9,49 @@ import platform
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
def is_docker_installed(common):
|
def is_docker_installed(global_common):
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
# Does the docker binary exist?
|
# Does the docker binary exist?
|
||||||
if os.path.isdir("/Applications/Docker.app") and os.path.exists(
|
if os.path.isdir("/Applications/Docker.app") and os.path.exists(
|
||||||
common.container_runtime
|
global_common.container_runtime
|
||||||
):
|
):
|
||||||
# Is it executable?
|
# Is it executable?
|
||||||
st = os.stat(common.container_runtime)
|
st = os.stat(global_common.container_runtime)
|
||||||
return bool(st.st_mode & stat.S_IXOTH)
|
return bool(st.st_mode & stat.S_IXOTH)
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
return os.path.exists(common.container_runtime)
|
return os.path.exists(global_common.container_runtime)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def is_docker_ready(common):
|
def is_docker_ready(global_common):
|
||||||
# Run `docker ps` without an error
|
# Run `docker ps` without an error
|
||||||
try:
|
try:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[common.container_runtime, "ps"],
|
[global_common.container_runtime, "ps"],
|
||||||
check=True,
|
check=True,
|
||||||
startupinfo=common.get_subprocess_startupinfo(),
|
startupinfo=global_common.get_subprocess_startupinfo(),
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def launch_docker_windows(common):
|
def launch_docker_windows(global_common):
|
||||||
docker_desktop_path = "C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"
|
docker_desktop_path = "C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"
|
||||||
subprocess.Popen(
|
subprocess.Popen(
|
||||||
[docker_desktop_path], startupinfo=common.get_subprocess_startupinfo()
|
[docker_desktop_path], startupinfo=global_common.get_subprocess_startupinfo()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DockerInstaller(QtWidgets.QDialog):
|
class DockerInstaller(QtWidgets.QDialog):
|
||||||
def __init__(self, common):
|
def __init__(self, global_common):
|
||||||
super(DockerInstaller, self).__init__()
|
super(DockerInstaller, self).__init__()
|
||||||
self.common = common
|
self.global_common = global_common
|
||||||
|
|
||||||
self.setWindowTitle("dangerzone")
|
self.setWindowTitle("dangerzone")
|
||||||
self.setWindowIcon(self.common.get_window_icon())
|
self.setWindowIcon(self.global_common.get_window_icon())
|
||||||
self.setMinimumHeight(170)
|
self.setMinimumHeight(170)
|
||||||
|
|
||||||
label = QtWidgets.QLabel()
|
label = QtWidgets.QLabel()
|
||||||
|
|
451
dangerzone/global_common.py
Normal file
451
dangerzone/global_common.py
Normal file
|
@ -0,0 +1,451 @@
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import inspect
|
||||||
|
import tempfile
|
||||||
|
import appdirs
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
import shlex
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
import CoreServices
|
||||||
|
import LaunchServices
|
||||||
|
import plistlib
|
||||||
|
|
||||||
|
elif platform.system() == "Linux":
|
||||||
|
import grp
|
||||||
|
import getpass
|
||||||
|
from xdg.DesktopEntry import DesktopEntry
|
||||||
|
|
||||||
|
from .settings import Settings
|
||||||
|
|
||||||
|
|
||||||
|
class GlobalCommon(object):
|
||||||
|
"""
|
||||||
|
The GlobalCommon class is a singleton of shared functionality throughout the app
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
# Qt app
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
# Temporary directory to store pixel data
|
||||||
|
# Note in macOS, temp dirs must be in /tmp (or a few other paths) for Docker to mount them
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
self.pixel_dir = tempfile.TemporaryDirectory(prefix="dangerzone-pixel-")
|
||||||
|
self.safe_dir = tempfile.TemporaryDirectory(prefix="dangerzone-safe-")
|
||||||
|
else:
|
||||||
|
self.pixel_dir = tempfile.TemporaryDirectory(
|
||||||
|
prefix="/tmp/dangerzone-pixel-"
|
||||||
|
)
|
||||||
|
self.safe_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-safe-")
|
||||||
|
print(
|
||||||
|
f"Temporary directories created, dangerous={self.pixel_dir.name}, safe={self.safe_dir.name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Name of input file
|
||||||
|
self.document_filename = None
|
||||||
|
|
||||||
|
# Name of output file
|
||||||
|
self.save_filename = None
|
||||||
|
|
||||||
|
# Preload font
|
||||||
|
self.fixed_font = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)
|
||||||
|
|
||||||
|
# App data folder
|
||||||
|
self.appdata_path = appdirs.user_config_dir("dangerzone")
|
||||||
|
|
||||||
|
# Container runtime
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
self.container_runtime = "/usr/local/bin/docker"
|
||||||
|
elif platform.system() == "Windows":
|
||||||
|
self.container_runtime = (
|
||||||
|
"C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Linux
|
||||||
|
|
||||||
|
# If this is fedora-like, use podman
|
||||||
|
if os.path.exists("/usr/bin/dnf"):
|
||||||
|
self.container_runtime = "/usr/bin/podman"
|
||||||
|
# Otherwise, use docker
|
||||||
|
else:
|
||||||
|
self.container_runtime = "/usr/bin/docker"
|
||||||
|
|
||||||
|
# Preload list of PDF viewers on computer
|
||||||
|
self.pdf_viewers = self._find_pdf_viewers()
|
||||||
|
|
||||||
|
# Languages supported by tesseract
|
||||||
|
self.ocr_languages = {
|
||||||
|
"Afrikaans": "ar",
|
||||||
|
"Albanian": "sqi",
|
||||||
|
"Amharic": "amh",
|
||||||
|
"Arabic": "ara",
|
||||||
|
"Arabic script": "Arabic",
|
||||||
|
"Armenian": "hye",
|
||||||
|
"Armenian script": "Armenian",
|
||||||
|
"Assamese": "asm",
|
||||||
|
"Azerbaijani": "aze",
|
||||||
|
"Azerbaijani (Cyrillic)": "aze_cyrl",
|
||||||
|
"Basque": "eus",
|
||||||
|
"Belarusian": "bel",
|
||||||
|
"Bengali": "ben",
|
||||||
|
"Bengali script": "Bengali",
|
||||||
|
"Bosnian": "bos",
|
||||||
|
"Breton": "bre",
|
||||||
|
"Bulgarian": "bul",
|
||||||
|
"Burmese": "mya",
|
||||||
|
"Canadian Aboriginal script": "Canadian_Aboriginal",
|
||||||
|
"Catalan": "cat",
|
||||||
|
"Cebuano": "ceb",
|
||||||
|
"Cherokee": "chr",
|
||||||
|
"Cherokee script": "Cherokee",
|
||||||
|
"Chinese - Simplified": "chi_sim",
|
||||||
|
"Chinese - Simplified (vertical)": "chi_sim_vert",
|
||||||
|
"Chinese - Traditional": "chi_tra",
|
||||||
|
"Chinese - Traditional (vertical)": "chi_tra_vert",
|
||||||
|
"Corsican": "cos",
|
||||||
|
"Croatian": "hrv",
|
||||||
|
"Cyrillic script": "Cyrillic",
|
||||||
|
"Czech": "ces",
|
||||||
|
"Danish": "dan",
|
||||||
|
"Devanagari script": "Devanagari",
|
||||||
|
"Divehi": "div",
|
||||||
|
"Dutch": "nld",
|
||||||
|
"Dzongkha": "dzo",
|
||||||
|
"English": "eng",
|
||||||
|
"English, Middle (1100-1500)": "enm",
|
||||||
|
"Esperanto": "epo",
|
||||||
|
"Estonian": "est",
|
||||||
|
"Ethiopic script": "Ethiopic",
|
||||||
|
"Faroese": "fao",
|
||||||
|
"Filipino": "fil",
|
||||||
|
"Finnish": "fin",
|
||||||
|
"Fraktur script": "Fraktur",
|
||||||
|
"Frankish": "frk",
|
||||||
|
"French": "fra",
|
||||||
|
"French, Middle (ca.1400-1600)": "frm",
|
||||||
|
"Frisian (Western)": "fry",
|
||||||
|
"Gaelic (Scots)": "gla",
|
||||||
|
"Galician": "glg",
|
||||||
|
"Georgian": "kat",
|
||||||
|
"Georgian script": "Georgian",
|
||||||
|
"German": "deu",
|
||||||
|
"Greek": "ell",
|
||||||
|
"Greek script": "Greek",
|
||||||
|
"Gujarati": "guj",
|
||||||
|
"Gujarati script": "Gujarati",
|
||||||
|
"Gurmukhi script": "Gurmukhi",
|
||||||
|
"Hangul script": "Hangul",
|
||||||
|
"Hangul (vertical) script": "Hangul_vert",
|
||||||
|
"Han - Simplified script": "HanS",
|
||||||
|
"Han - Simplified (vertical) script": "HanS_vert",
|
||||||
|
"Han - Traditional script": "HanT",
|
||||||
|
"Han - Traditional (vertical) script": "HanT_vert",
|
||||||
|
"Hatian": "hat",
|
||||||
|
"Hebrew": "heb",
|
||||||
|
"Hebrew script": "Hebrew",
|
||||||
|
"Hindi": "hin",
|
||||||
|
"Hungarian": "hun",
|
||||||
|
"Icelandic": "isl",
|
||||||
|
"Indonesian": "ind",
|
||||||
|
"Inuktitut": "iku",
|
||||||
|
"Irish": "gle",
|
||||||
|
"Italian": "ita",
|
||||||
|
"Italian - Old": "ita_old",
|
||||||
|
"Japanese": "jpn",
|
||||||
|
"Japanese script": "Japanese",
|
||||||
|
"Japanese (vertical)": "jpn_vert",
|
||||||
|
"Japanese (vertical) script": "Japanese_vert",
|
||||||
|
"Javanese": "jav",
|
||||||
|
"Kannada": "kan",
|
||||||
|
"Kannada script": "Kannada",
|
||||||
|
"Kazakh": "kaz",
|
||||||
|
"Khmer": "khm",
|
||||||
|
"Khmer script": "Khmer",
|
||||||
|
"Korean": "kor",
|
||||||
|
"Korean (vertical)": "kor_vert",
|
||||||
|
"Kurdish (Arabic)": "kur_ara",
|
||||||
|
"Kyrgyz": "kir",
|
||||||
|
"Lao": "lao",
|
||||||
|
"Lao script": "Lao",
|
||||||
|
"Latin": "lat",
|
||||||
|
"Latin script": "Latin",
|
||||||
|
"Latvian": "lav",
|
||||||
|
"Lithuanian": "lit",
|
||||||
|
"Luxembourgish": "ltz",
|
||||||
|
"Macedonian": "mkd",
|
||||||
|
"Malayalam": "mal",
|
||||||
|
"Malayalam script": "Malayalam",
|
||||||
|
"Malay": "msa",
|
||||||
|
"Maltese": "mlt",
|
||||||
|
"Maori": "mri",
|
||||||
|
"Marathi": "mar",
|
||||||
|
"Mongolian": "mon",
|
||||||
|
"Myanmar script": "Myanmar",
|
||||||
|
"Nepali": "nep",
|
||||||
|
"Norwegian": "nor",
|
||||||
|
"Occitan (post 1500)": "oci",
|
||||||
|
"Old Georgian": "kat_old",
|
||||||
|
"Oriya (Odia) script": "Oriya",
|
||||||
|
"Oriya": "ori",
|
||||||
|
"Pashto": "pus",
|
||||||
|
"Persian": "fas",
|
||||||
|
"Polish": "pol",
|
||||||
|
"Portuguese": "por",
|
||||||
|
"Punjabi": "pan",
|
||||||
|
"Quechua": "que",
|
||||||
|
"Romanian": "ron",
|
||||||
|
"Russian": "rus",
|
||||||
|
"Sanskrit": "san",
|
||||||
|
"script and orientation": "osd",
|
||||||
|
"Serbian (Latin)": "srp_latn",
|
||||||
|
"Serbian": "srp",
|
||||||
|
"Sindhi": "snd",
|
||||||
|
"Sinhala script": "Sinhala",
|
||||||
|
"Sinhala": "sin",
|
||||||
|
"Slovakian": "slk",
|
||||||
|
"Slovenian": "slv",
|
||||||
|
"Spanish, Castilian - Old": "spa_old",
|
||||||
|
"Spanish": "spa",
|
||||||
|
"Sundanese": "sun",
|
||||||
|
"Swahili": "swa",
|
||||||
|
"Swedish": "swe",
|
||||||
|
"Syriac script": "Syriac",
|
||||||
|
"Syriac": "syr",
|
||||||
|
"Tajik": "tgk",
|
||||||
|
"Tamil script": "Tamil",
|
||||||
|
"Tamil": "tam",
|
||||||
|
"Tatar": "tat",
|
||||||
|
"Telugu script": "Telugu",
|
||||||
|
"Telugu": "tel",
|
||||||
|
"Thaana script": "Thaana",
|
||||||
|
"Thai script": "Thai",
|
||||||
|
"Thai": "tha",
|
||||||
|
"Tibetan script": "Tibetan",
|
||||||
|
"Tibetan Standard": "bod",
|
||||||
|
"Tigrinya": "tir",
|
||||||
|
"Tonga": "ton",
|
||||||
|
"Turkish": "tur",
|
||||||
|
"Ukrainian": "ukr",
|
||||||
|
"Urdu": "urd",
|
||||||
|
"Uyghur": "uig",
|
||||||
|
"Uzbek (Cyrillic)": "uzb_cyrl",
|
||||||
|
"Uzbek": "uzb",
|
||||||
|
"Vietnamese script": "Vietnamese",
|
||||||
|
"Vietnamese": "vie",
|
||||||
|
"Welsh": "cym",
|
||||||
|
"Yiddish": "yid",
|
||||||
|
"Yoruba": "yor",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load settings
|
||||||
|
self.settings = Settings(self)
|
||||||
|
|
||||||
|
def get_resource_path(self, filename):
|
||||||
|
if getattr(sys, "dangerzone_dev", False):
|
||||||
|
# Look for resources directory relative to python file
|
||||||
|
prefix = os.path.join(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"share",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
prefix = os.path.join(
|
||||||
|
os.path.dirname(os.path.dirname(sys.executable)), "Resources/share"
|
||||||
|
)
|
||||||
|
elif platform.system() == "Linux":
|
||||||
|
prefix = os.path.join(sys.prefix, "share", "dangerzone")
|
||||||
|
else:
|
||||||
|
# Windows
|
||||||
|
prefix = os.path.join(os.path.dirname(sys.executable), "share")
|
||||||
|
|
||||||
|
resource_path = os.path.join(prefix, filename)
|
||||||
|
return resource_path
|
||||||
|
|
||||||
|
def get_window_icon(self):
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
path = self.get_resource_path("dangerzone.ico")
|
||||||
|
else:
|
||||||
|
path = self.get_resource_path("logo.png")
|
||||||
|
return QtGui.QIcon(path)
|
||||||
|
|
||||||
|
def open_pdf_viewer(self, filename):
|
||||||
|
if self.settings.get("open_app") in self.pdf_viewers:
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
# Get the PDF reader bundle command
|
||||||
|
bundle_identifier = self.pdf_viewers[self.settings.get("open_app")]
|
||||||
|
args = ["open", "-b", bundle_identifier, filename]
|
||||||
|
|
||||||
|
# Run
|
||||||
|
print(f"Executing: {' '.join(args)}")
|
||||||
|
subprocess.run(args)
|
||||||
|
|
||||||
|
elif platform.system() == "Linux":
|
||||||
|
# Get the PDF reader command
|
||||||
|
args = shlex.split(self.pdf_viewers[self.settings.get("open_app")])
|
||||||
|
# %f, %F, %u, and %U are filenames or URLS -- so replace with the file to open
|
||||||
|
for i in range(len(args)):
|
||||||
|
if (
|
||||||
|
args[i] == "%f"
|
||||||
|
or args[i] == "%F"
|
||||||
|
or args[i] == "%u"
|
||||||
|
or args[i] == "%U"
|
||||||
|
):
|
||||||
|
args[i] = filename
|
||||||
|
|
||||||
|
# Open as a background process
|
||||||
|
print(f"Executing: {' '.join(args)}")
|
||||||
|
subprocess.Popen(args)
|
||||||
|
|
||||||
|
def _find_pdf_viewers(self):
|
||||||
|
pdf_viewers = {}
|
||||||
|
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
# Get all installed apps that can open PDFs
|
||||||
|
bundle_identifiers = LaunchServices.LSCopyAllRoleHandlersForContentType(
|
||||||
|
"com.adobe.pdf", CoreServices.kLSRolesAll
|
||||||
|
)
|
||||||
|
for bundle_identifier in bundle_identifiers:
|
||||||
|
# Get the filesystem path of the app
|
||||||
|
res = LaunchServices.LSCopyApplicationURLsForBundleIdentifier(
|
||||||
|
bundle_identifier, None
|
||||||
|
)
|
||||||
|
if res[0] is None:
|
||||||
|
continue
|
||||||
|
app_url = res[0][0]
|
||||||
|
app_path = str(app_url.path())
|
||||||
|
|
||||||
|
# Load its plist file
|
||||||
|
plist_path = os.path.join(app_path, "Contents/Info.plist")
|
||||||
|
with open(plist_path, "rb") as f:
|
||||||
|
plist_data = f.read()
|
||||||
|
plist_dict = plistlib.loads(plist_data)
|
||||||
|
|
||||||
|
if plist_dict["CFBundleName"] != "Dangerzone":
|
||||||
|
pdf_viewers[plist_dict["CFBundleName"]] = bundle_identifier
|
||||||
|
|
||||||
|
elif platform.system() == "Linux":
|
||||||
|
# Find all .desktop files
|
||||||
|
for search_path in [
|
||||||
|
"/usr/share/applications",
|
||||||
|
"/usr/local/share/applications",
|
||||||
|
os.path.expanduser("~/.local/share/applications"),
|
||||||
|
]:
|
||||||
|
try:
|
||||||
|
for filename in os.listdir(search_path):
|
||||||
|
full_filename = os.path.join(search_path, filename)
|
||||||
|
if os.path.splitext(filename)[1] == ".desktop":
|
||||||
|
|
||||||
|
# See which ones can open PDFs
|
||||||
|
desktop_entry = DesktopEntry(full_filename)
|
||||||
|
if (
|
||||||
|
"application/pdf" in desktop_entry.getMimeTypes()
|
||||||
|
and desktop_entry.getName() != "dangerzone"
|
||||||
|
):
|
||||||
|
pdf_viewers[
|
||||||
|
desktop_entry.getName()
|
||||||
|
] = desktop_entry.getExec()
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return pdf_viewers
|
||||||
|
|
||||||
|
def ensure_user_is_in_docker_group(self):
|
||||||
|
try:
|
||||||
|
groupinfo = grp.getgrnam("docker")
|
||||||
|
except:
|
||||||
|
# Ignore if group is not found
|
||||||
|
return True
|
||||||
|
|
||||||
|
username = getpass.getuser()
|
||||||
|
if username not in groupinfo.gr_mem:
|
||||||
|
# User is not in docker group, so prompt about adding the user to the docker group
|
||||||
|
message = "<b>Dangerzone requires Docker.</b><br><br>Click Ok to add your user to the 'docker' group. You will have to type your login password."
|
||||||
|
if Alert(self, message).launch():
|
||||||
|
p = subprocess.run(
|
||||||
|
[
|
||||||
|
"/usr/bin/pkexec",
|
||||||
|
"/usr/sbin/usermod",
|
||||||
|
"-a",
|
||||||
|
"-G",
|
||||||
|
"docker",
|
||||||
|
username,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if p.returncode == 0:
|
||||||
|
message = "Great! Now you must log out of your computer and log back in, and then you can use Dangerzone."
|
||||||
|
Alert(self, message).launch()
|
||||||
|
else:
|
||||||
|
message = "Failed to add your user to the 'docker' group, quitting."
|
||||||
|
Alert(self, message).launch()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_subprocess_startupinfo(self):
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
|
return startupinfo
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class Alert(QtWidgets.QDialog):
|
||||||
|
def __init__(self, common, message):
|
||||||
|
super(Alert, self).__init__()
|
||||||
|
self.common = common
|
||||||
|
|
||||||
|
self.setWindowTitle("dangerzone")
|
||||||
|
self.setWindowIcon(self.common.get_window_icon())
|
||||||
|
self.setModal(True)
|
||||||
|
|
||||||
|
flags = (
|
||||||
|
QtCore.Qt.CustomizeWindowHint
|
||||||
|
| QtCore.Qt.WindowTitleHint
|
||||||
|
| QtCore.Qt.WindowSystemMenuHint
|
||||||
|
| QtCore.Qt.WindowCloseButtonHint
|
||||||
|
| QtCore.Qt.WindowStaysOnTopHint
|
||||||
|
)
|
||||||
|
self.setWindowFlags(flags)
|
||||||
|
|
||||||
|
logo = QtWidgets.QLabel()
|
||||||
|
logo.setPixmap(
|
||||||
|
QtGui.QPixmap.fromImage(
|
||||||
|
QtGui.QImage(self.common.get_resource_path("icon.png"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
label = QtWidgets.QLabel()
|
||||||
|
label.setText(message)
|
||||||
|
label.setWordWrap(True)
|
||||||
|
|
||||||
|
message_layout = QtWidgets.QHBoxLayout()
|
||||||
|
message_layout.addWidget(logo)
|
||||||
|
message_layout.addWidget(label)
|
||||||
|
|
||||||
|
ok_button = QtWidgets.QPushButton("Ok")
|
||||||
|
ok_button.clicked.connect(self.accept)
|
||||||
|
cancel_button = QtWidgets.QPushButton("Cancel")
|
||||||
|
cancel_button.clicked.connect(self.reject)
|
||||||
|
|
||||||
|
buttons_layout = QtWidgets.QHBoxLayout()
|
||||||
|
buttons_layout.addStretch()
|
||||||
|
buttons_layout.addWidget(ok_button)
|
||||||
|
buttons_layout.addWidget(cancel_button)
|
||||||
|
|
||||||
|
layout = QtWidgets.QVBoxLayout()
|
||||||
|
layout.addLayout(message_layout)
|
||||||
|
layout.addLayout(buttons_layout)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def launch(self):
|
||||||
|
return self.exec_() == QtWidgets.QDialog.Accepted
|
|
@ -1,19 +1,22 @@
|
||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
from .doc_selection_widget import DocSelectionWidget
|
from .doc_selection_widget import DocSelectionWidget
|
||||||
from .settings_widget import SettingsWidget
|
from .settings_widget import SettingsWidget
|
||||||
from .tasks_widget import TasksWidget
|
from .tasks_widget import TasksWidget
|
||||||
|
from .common import Common
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
class MainWindow(QtWidgets.QMainWindow):
|
||||||
def __init__(self, common):
|
def __init__(self, global_common):
|
||||||
super(MainWindow, self).__init__()
|
super(MainWindow, self).__init__()
|
||||||
self.common = common
|
self.global_common = global_common
|
||||||
|
self.common = Common()
|
||||||
|
|
||||||
self.setWindowTitle("dangerzone")
|
self.setWindowTitle("dangerzone")
|
||||||
self.setWindowIcon(self.common.get_window_icon())
|
self.setWindowIcon(self.global_common.get_window_icon())
|
||||||
|
|
||||||
self.setMinimumWidth(600)
|
self.setMinimumWidth(600)
|
||||||
self.setMinimumHeight(400)
|
self.setMinimumHeight(400)
|
||||||
|
@ -22,11 +25,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
logo = QtWidgets.QLabel()
|
logo = QtWidgets.QLabel()
|
||||||
logo.setPixmap(
|
logo.setPixmap(
|
||||||
QtGui.QPixmap.fromImage(
|
QtGui.QPixmap.fromImage(
|
||||||
QtGui.QImage(self.common.get_resource_path("icon.png"))
|
QtGui.QImage(self.global_common.get_resource_path("icon.png"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
header_label = QtWidgets.QLabel("dangerzone")
|
header_label = QtWidgets.QLabel("dangerzone")
|
||||||
header_label.setFont(self.common.fixed_font)
|
header_label.setFont(self.global_common.fixed_font)
|
||||||
header_label.setStyleSheet("QLabel { font-weight: bold; font-size: 50px; }")
|
header_label.setStyleSheet("QLabel { font-weight: bold; font-size: 50px; }")
|
||||||
header_layout = QtWidgets.QHBoxLayout()
|
header_layout = QtWidgets.QHBoxLayout()
|
||||||
header_layout.addStretch()
|
header_layout.addStretch()
|
||||||
|
@ -41,7 +44,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
self.doc_selection_widget.show()
|
self.doc_selection_widget.show()
|
||||||
|
|
||||||
# Settings
|
# Settings
|
||||||
self.settings_widget = SettingsWidget(self.common)
|
self.settings_widget = SettingsWidget(self.global_common, self.common)
|
||||||
self.doc_selection_widget.document_selected.connect(
|
self.doc_selection_widget.document_selected.connect(
|
||||||
self.settings_widget.document_selected
|
self.settings_widget.document_selected
|
||||||
)
|
)
|
||||||
|
@ -49,7 +52,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
self.settings_widget.hide()
|
self.settings_widget.hide()
|
||||||
|
|
||||||
# Tasks
|
# Tasks
|
||||||
self.tasks_widget = TasksWidget(self.common)
|
self.tasks_widget = TasksWidget(self.global_common, self.common)
|
||||||
|
self.tasks_widget.close_window.connect(self.close)
|
||||||
self.doc_selection_widget.document_selected.connect(
|
self.doc_selection_widget.document_selected.connect(
|
||||||
self.tasks_widget.document_selected
|
self.tasks_widget.document_selected
|
||||||
)
|
)
|
||||||
|
@ -79,3 +83,5 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
|
|
||||||
def closeEvent(self, e):
|
def closeEvent(self, e):
|
||||||
e.accept()
|
e.accept()
|
||||||
|
if platform.system() != "Darwin":
|
||||||
|
self.global_common.app.quit()
|
||||||
|
|
|
@ -7,8 +7,9 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
class SettingsWidget(QtWidgets.QWidget):
|
class SettingsWidget(QtWidgets.QWidget):
|
||||||
start_clicked = QtCore.pyqtSignal()
|
start_clicked = QtCore.pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, common):
|
def __init__(self, global_common, common):
|
||||||
super(SettingsWidget, self).__init__()
|
super(SettingsWidget, self).__init__()
|
||||||
|
self.global_common = global_common
|
||||||
self.common = common
|
self.common = common
|
||||||
|
|
||||||
# Dangerous document label
|
# Dangerous document label
|
||||||
|
@ -47,9 +48,9 @@ class SettingsWidget(QtWidgets.QWidget):
|
||||||
)
|
)
|
||||||
self.open_checkbox.clicked.connect(self.update_ui)
|
self.open_checkbox.clicked.connect(self.update_ui)
|
||||||
self.open_combobox = QtWidgets.QComboBox()
|
self.open_combobox = QtWidgets.QComboBox()
|
||||||
for k in self.common.pdf_viewers:
|
for k in self.global_common.pdf_viewers:
|
||||||
self.open_combobox.addItem(
|
self.open_combobox.addItem(
|
||||||
k, QtCore.QVariant(self.common.pdf_viewers[k])
|
k, QtCore.QVariant(self.global_common.pdf_viewers[k])
|
||||||
)
|
)
|
||||||
open_layout = QtWidgets.QHBoxLayout()
|
open_layout = QtWidgets.QHBoxLayout()
|
||||||
open_layout.addWidget(self.open_checkbox)
|
open_layout.addWidget(self.open_checkbox)
|
||||||
|
@ -59,8 +60,10 @@ class SettingsWidget(QtWidgets.QWidget):
|
||||||
# OCR document
|
# OCR document
|
||||||
self.ocr_checkbox = QtWidgets.QCheckBox("OCR document, language")
|
self.ocr_checkbox = QtWidgets.QCheckBox("OCR document, language")
|
||||||
self.ocr_combobox = QtWidgets.QComboBox()
|
self.ocr_combobox = QtWidgets.QComboBox()
|
||||||
for k in self.common.ocr_languages:
|
for k in self.global_common.ocr_languages:
|
||||||
self.ocr_combobox.addItem(k, QtCore.QVariant(self.common.ocr_languages[k]))
|
self.ocr_combobox.addItem(
|
||||||
|
k, QtCore.QVariant(self.global_common.ocr_languages[k])
|
||||||
|
)
|
||||||
ocr_layout = QtWidgets.QHBoxLayout()
|
ocr_layout = QtWidgets.QHBoxLayout()
|
||||||
ocr_layout.addWidget(self.ocr_checkbox)
|
ocr_layout.addWidget(self.ocr_checkbox)
|
||||||
ocr_layout.addWidget(self.ocr_combobox)
|
ocr_layout.addWidget(self.ocr_combobox)
|
||||||
|
@ -98,39 +101,43 @@ class SettingsWidget(QtWidgets.QWidget):
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
# Load values from settings
|
# Load values from settings
|
||||||
if self.common.settings.get("save"):
|
if self.global_common.settings.get("save"):
|
||||||
self.save_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.save_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
else:
|
else:
|
||||||
self.save_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
self.save_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
if self.common.settings.get("ocr"):
|
if self.global_common.settings.get("ocr"):
|
||||||
self.ocr_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.ocr_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
else:
|
else:
|
||||||
self.ocr_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
self.ocr_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
index = self.ocr_combobox.findText(self.common.settings.get("ocr_language"))
|
index = self.ocr_combobox.findText(
|
||||||
|
self.global_common.settings.get("ocr_language")
|
||||||
|
)
|
||||||
if index != -1:
|
if index != -1:
|
||||||
self.ocr_combobox.setCurrentIndex(index)
|
self.ocr_combobox.setCurrentIndex(index)
|
||||||
|
|
||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
if self.common.settings.get("open"):
|
if self.global_common.settings.get("open"):
|
||||||
self.open_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.open_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
else:
|
else:
|
||||||
self.open_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
self.open_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
index = self.open_combobox.findText(self.common.settings.get("open_app"))
|
index = self.open_combobox.findText(
|
||||||
|
self.global_common.settings.get("open_app")
|
||||||
|
)
|
||||||
if index != -1:
|
if index != -1:
|
||||||
self.open_combobox.setCurrentIndex(index)
|
self.open_combobox.setCurrentIndex(index)
|
||||||
|
|
||||||
if self.common.settings.get("update_container"):
|
if self.global_common.settings.get("update_container"):
|
||||||
self.update_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.update_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
else:
|
else:
|
||||||
self.update_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
self.update_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
# Is update containers required?
|
# Is update containers required?
|
||||||
output = subprocess.check_output(
|
output = subprocess.check_output(
|
||||||
[self.common.container_runtime, "image", "ls", "dangerzone"],
|
[self.global_common.container_runtime, "image", "ls", "dangerzone"],
|
||||||
startupinfo=self.common.get_subprocess_startupinfo(),
|
startupinfo=self.global_common.get_subprocess_startupinfo(),
|
||||||
)
|
)
|
||||||
if b"dangerzone" not in output:
|
if b"dangerzone" not in output:
|
||||||
self.update_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.update_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
|
@ -158,10 +165,9 @@ class SettingsWidget(QtWidgets.QWidget):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update the save location
|
# Update the save location
|
||||||
self.common.save_filename = (
|
save_filename = f"{os.path.splitext(self.common.document_filename)[0]}-safe.pdf"
|
||||||
f"{os.path.splitext(self.common.document_filename)[0]}-safe.pdf"
|
self.common.save_filename = save_filename
|
||||||
)
|
self.save_lineedit.setText(os.path.basename(save_filename))
|
||||||
self.save_lineedit.setText(os.path.basename(self.common.save_filename))
|
|
||||||
|
|
||||||
def save_browse_button_clicked(self):
|
def save_browse_button_clicked(self):
|
||||||
filename = QtWidgets.QFileDialog.getSaveFileName(
|
filename = QtWidgets.QFileDialog.getSaveFileName(
|
||||||
|
@ -176,22 +182,24 @@ class SettingsWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
def start_button_clicked(self):
|
def start_button_clicked(self):
|
||||||
# Update settings
|
# Update settings
|
||||||
self.common.settings.set(
|
self.global_common.settings.set(
|
||||||
"save", self.save_checkbox.checkState() == QtCore.Qt.Checked
|
"save", self.save_checkbox.checkState() == QtCore.Qt.Checked
|
||||||
)
|
)
|
||||||
self.common.settings.set(
|
self.global_common.settings.set(
|
||||||
"ocr", self.ocr_checkbox.checkState() == QtCore.Qt.Checked
|
"ocr", self.ocr_checkbox.checkState() == QtCore.Qt.Checked
|
||||||
)
|
)
|
||||||
self.common.settings.set("ocr_language", self.ocr_combobox.currentText())
|
self.global_common.settings.set("ocr_language", self.ocr_combobox.currentText())
|
||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
self.common.settings.set(
|
self.global_common.settings.set(
|
||||||
"open", self.open_checkbox.checkState() == QtCore.Qt.Checked
|
"open", self.open_checkbox.checkState() == QtCore.Qt.Checked
|
||||||
)
|
)
|
||||||
self.common.settings.set("open_app", self.open_combobox.currentText())
|
self.global_common.settings.set(
|
||||||
self.common.settings.set(
|
"open_app", self.open_combobox.currentText()
|
||||||
|
)
|
||||||
|
self.global_common.settings.set(
|
||||||
"update_container", self.update_checkbox.checkState() == QtCore.Qt.Checked
|
"update_container", self.update_checkbox.checkState() == QtCore.Qt.Checked
|
||||||
)
|
)
|
||||||
self.common.settings.save()
|
self.global_common.settings.save()
|
||||||
|
|
||||||
# Start!
|
# Start!
|
||||||
self.start_clicked.emit()
|
self.start_clicked.emit()
|
||||||
|
|
|
@ -16,7 +16,7 @@ class TaskBase(QtCore.QThread):
|
||||||
super(TaskBase, self).__init__()
|
super(TaskBase, self).__init__()
|
||||||
|
|
||||||
def exec_container(self, args, watch="stdout"):
|
def exec_container(self, args, watch="stdout"):
|
||||||
args = [self.common.container_runtime] + args
|
args = [self.global_common.container_runtime] + args
|
||||||
args_str = " ".join(pipes.quote(s) for s in args)
|
args_str = " ".join(pipes.quote(s) for s in args)
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
@ -31,7 +31,7 @@ class TaskBase(QtCore.QThread):
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
bufsize=1,
|
bufsize=1,
|
||||||
universal_newlines=True,
|
universal_newlines=True,
|
||||||
startupinfo=self.common.get_subprocess_startupinfo(),
|
startupinfo=self.global_common.get_subprocess_startupinfo(),
|
||||||
) as p:
|
) as p:
|
||||||
if watch == "stdout":
|
if watch == "stdout":
|
||||||
pipe = p.stdout
|
pipe = p.stdout
|
||||||
|
@ -53,8 +53,9 @@ class TaskBase(QtCore.QThread):
|
||||||
|
|
||||||
|
|
||||||
class PullImageTask(TaskBase):
|
class PullImageTask(TaskBase):
|
||||||
def __init__(self, common):
|
def __init__(self, global_common, common):
|
||||||
super(PullImageTask, self).__init__()
|
super(PullImageTask, self).__init__()
|
||||||
|
self.global_common = global_common
|
||||||
self.common = common
|
self.common = common
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -71,12 +72,13 @@ class PullImageTask(TaskBase):
|
||||||
|
|
||||||
|
|
||||||
class BuildContainerTask(TaskBase):
|
class BuildContainerTask(TaskBase):
|
||||||
def __init__(self, common):
|
def __init__(self, global_common, common):
|
||||||
super(BuildContainerTask, self).__init__()
|
super(BuildContainerTask, self).__init__()
|
||||||
|
self.global_common = global_common
|
||||||
self.common = common
|
self.common = common
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
container_path = self.common.get_resource_path("container")
|
container_path = self.global_common.get_resource_path("container")
|
||||||
self.update_label.emit("Building container (this might take a long time)")
|
self.update_label.emit("Building container (this might take a long time)")
|
||||||
self.update_details.emit("")
|
self.update_details.emit("")
|
||||||
args = ["build", "-t", "dangerzone", container_path]
|
args = ["build", "-t", "dangerzone", container_path]
|
||||||
|
@ -90,8 +92,9 @@ class BuildContainerTask(TaskBase):
|
||||||
|
|
||||||
|
|
||||||
class ConvertToPixels(TaskBase):
|
class ConvertToPixels(TaskBase):
|
||||||
def __init__(self, common):
|
def __init__(self, global_common, common):
|
||||||
super(ConvertToPixels, self).__init__()
|
super(ConvertToPixels, self).__init__()
|
||||||
|
self.global_common = global_common
|
||||||
self.common = common
|
self.common = common
|
||||||
|
|
||||||
self.max_image_width = 10000
|
self.max_image_width = 10000
|
||||||
|
@ -183,8 +186,9 @@ class ConvertToPixels(TaskBase):
|
||||||
|
|
||||||
|
|
||||||
class ConvertToPDF(TaskBase):
|
class ConvertToPDF(TaskBase):
|
||||||
def __init__(self, common):
|
def __init__(self, global_common, common):
|
||||||
super(ConvertToPDF, self).__init__()
|
super(ConvertToPDF, self).__init__()
|
||||||
|
self.global_common = global_common
|
||||||
self.common = common
|
self.common = common
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -192,13 +196,13 @@ class ConvertToPDF(TaskBase):
|
||||||
|
|
||||||
# Build environment variables list
|
# Build environment variables list
|
||||||
envs = []
|
envs = []
|
||||||
if self.common.settings.get("ocr"):
|
if self.global_common.settings.get("ocr"):
|
||||||
envs += ["-e", "OCR=1"]
|
envs += ["-e", "OCR=1"]
|
||||||
else:
|
else:
|
||||||
envs += ["-e", "OCR=0"]
|
envs += ["-e", "OCR=0"]
|
||||||
envs += [
|
envs += [
|
||||||
"-e",
|
"-e",
|
||||||
f"OCR_LANGUAGE={self.common.ocr_languages[self.common.settings.get('ocr_language')]}",
|
f"OCR_LANGUAGE={self.global_common.ocr_languages[self.global_common.settings.get('ocr_language')]}",
|
||||||
]
|
]
|
||||||
|
|
||||||
args = (
|
args = (
|
||||||
|
|
|
@ -9,8 +9,11 @@ from .tasks import PullImageTask, BuildContainerTask, ConvertToPixels, ConvertTo
|
||||||
|
|
||||||
|
|
||||||
class TasksWidget(QtWidgets.QWidget):
|
class TasksWidget(QtWidgets.QWidget):
|
||||||
def __init__(self, common):
|
close_window = QtCore.pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, global_common, common):
|
||||||
super(TasksWidget, self).__init__()
|
super(TasksWidget, self).__init__()
|
||||||
|
self.global_common = global_common
|
||||||
self.common = common
|
self.common = common
|
||||||
|
|
||||||
# Dangerous document label
|
# Dangerous document label
|
||||||
|
@ -28,7 +31,7 @@ class TasksWidget(QtWidgets.QWidget):
|
||||||
self.task_details.setStyleSheet(
|
self.task_details.setStyleSheet(
|
||||||
"QLabel { background-color: #ffffff; font-size: 12px; padding: 10px; }"
|
"QLabel { background-color: #ffffff; font-size: 12px; padding: 10px; }"
|
||||||
)
|
)
|
||||||
self.task_details.setFont(self.common.fixed_font)
|
self.task_details.setFont(self.global_common.fixed_font)
|
||||||
self.task_details.setAlignment(QtCore.Qt.AlignTop)
|
self.task_details.setAlignment(QtCore.Qt.AlignTop)
|
||||||
|
|
||||||
self.details_scrollarea = QtWidgets.QScrollArea()
|
self.details_scrollarea = QtWidgets.QScrollArea()
|
||||||
|
@ -55,7 +58,7 @@ class TasksWidget(QtWidgets.QWidget):
|
||||||
)
|
)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if self.common.settings.get("update_container"):
|
if self.global_common.settings.get("update_container"):
|
||||||
self.tasks += [PullImageTask, BuildContainerTask]
|
self.tasks += [PullImageTask, BuildContainerTask]
|
||||||
self.tasks += [ConvertToPixels, ConvertToPDF]
|
self.tasks += [ConvertToPixels, ConvertToPDF]
|
||||||
self.next_task()
|
self.next_task()
|
||||||
|
@ -67,7 +70,7 @@ class TasksWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
self.task_details.setText("")
|
self.task_details.setText("")
|
||||||
|
|
||||||
self.current_task = self.tasks.pop(0)(self.common)
|
self.current_task = self.tasks.pop(0)(self.global_common, self.common)
|
||||||
self.current_task.update_label.connect(self.update_label)
|
self.current_task.update_label.connect(self.update_label)
|
||||||
self.current_task.update_details.connect(self.update_details)
|
self.current_task.update_details.connect(self.update_details)
|
||||||
self.current_task.task_finished.connect(self.next_task)
|
self.current_task.task_finished.connect(self.next_task)
|
||||||
|
@ -91,7 +94,7 @@ class TasksWidget(QtWidgets.QWidget):
|
||||||
def all_done(self):
|
def all_done(self):
|
||||||
# Save safe PDF
|
# Save safe PDF
|
||||||
source_filename = f"{self.common.safe_dir.name}/safe-output-compressed.pdf"
|
source_filename = f"{self.common.safe_dir.name}/safe-output-compressed.pdf"
|
||||||
if self.common.settings.get("save"):
|
if self.global_common.settings.get("save"):
|
||||||
dest_filename = self.common.save_filename
|
dest_filename = self.common.save_filename
|
||||||
else:
|
else:
|
||||||
# If not saving, then save it to a temp file instead
|
# If not saving, then save it to a temp file instead
|
||||||
|
@ -107,15 +110,19 @@ class TasksWidget(QtWidgets.QWidget):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Open
|
# Open
|
||||||
if self.common.settings.get("open"):
|
if self.global_common.settings.get("open"):
|
||||||
self.common.open_pdf_viewer(dest_filename)
|
self.global_common.open_pdf_viewer(dest_filename)
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
self.common.pixel_dir.cleanup()
|
self.common.pixel_dir.cleanup()
|
||||||
self.common.safe_dir.cleanup()
|
self.common.safe_dir.cleanup()
|
||||||
|
|
||||||
# Quit
|
# Quit
|
||||||
self.common.app.quit()
|
if platform.system() == "Darwin":
|
||||||
|
# In macOS, just close the window
|
||||||
|
self.close_window.emit()
|
||||||
|
else:
|
||||||
|
self.global_common.app.quit()
|
||||||
|
|
||||||
def scroll_to_bottom(self, minimum, maximum):
|
def scroll_to_bottom(self, minimum, maximum):
|
||||||
self.details_scrollarea.verticalScrollBar().setValue(maximum)
|
self.details_scrollarea.verticalScrollBar().setValue(maximum)
|
||||||
|
|
Loading…
Reference in a new issue