Curtail icon indicating copy to clipboard operation
Curtail copied to clipboard

Architecture proposal. Decouple logic from UI.

Open akozlovskiy119 opened this issue 4 years ago • 1 comments

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:

CurtailArchitecture

Some examples and explanations:

  • Abstract Compressor consists of abstract compress method and abstract get_mime_type class 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 compress method 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.

akozlovskiy119 avatar Apr 08 '21 02:04 akozlovskiy119

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! :)

Huluti avatar Jun 05 '21 14:06 Huluti