Architecture proposal. Decouple logic from UI.
As I understand, almost all Curtail logic is tightly coupled with its UI in CurtailWindow and CurtailPrefsWindow. Compressor class is not good too because it mixes JPEG and PNG compression steps and adding more types will not be easy in the future. How about reworking Curtail architecture to something like this:

Some examples and explanations:
- Abstract Compressor consists of abstract
compressmethod and abstractget_mime_typeclass method that indicates what types can be compressed with it. All concrete compressors inherit from it.
from abc import ABC, abstractmethod
class CompressionError(Exception):
pass
class Compressor(ABC):
def __init__(self, settings_manager):
self.settings = settings_manager
@classmethod
@abstractmethod
def get_mime_type(cls):
pass
@abstractmethod
def compress(self, file_in, file_out):
pass
class JPEGCompressor(Compressor):
@classmethod
def get_mime_type(cls):
return 'image/jpeg'
def compress(self, file_in, file_out):
raise NotImplementedError()
# etc...
- All compression operations are handled through Compression Manager. It owns a registry of compressors that could be used. When passed a file, it automatically chooses which compressor to use based on input file mime type and launches its
compressmethod in a separate process. It also handles compression errors. EDIT: I can't yet describe how exactly the manager should notify the main window about compression completion.
class CompressionManager:
def __init__(self, settings_manager):
self.settings = settings_manager
self.compressors = {}
def register_compressor(self, ConcreteCompressor):
mime_type = ConcreteCompressor.get_mime_type()
if mime_type not in self.compressors:
self.compressors[mime_type] = ConcreteCompressor(self.settings)
def compress(self, file_in, file_out):
file_mime_type = get_file_mime_type(file_in)
try:
# Should be launched in a separate process
self.compressors[file_mime_type].compress(file_in, file_out)
except KeyError:
# No suitable compressor
...
except CompressionError:
# Internal compression error
...
# manager = CompressionManager(settings_manager)
# manager.register_compressor(JPEGCompressor)
# manager.compress("hello.jpg", "world.jpg") -> ok, has suitable compressor
# manager.compress("hello.tiff", "world.tiff") -> no suitable compressor
-
Input Checker is a small helper class (or function) to preprocess/prevalidate incoming filenames, for example for their existence, and pass them to the Manager. To be defined.
-
All settings related operations and errors are handled by Settings Manager. Also it can have some kind of access control so for example Compression Manager have access to getters, but not setters. To be defined.
Thank you for taking the time to think about a new structure. You are right about the fact that it will become necessary. I think that this will be for this summer... I keep this in mind and and I'll probably need some advice! :)