@LRD36: Anmerkungen zum Quelltext: `images` enthält gar keine Bilder sondern Dateinamen von Bildern. Das ist etwas verwirrend in einem Programm in dem es auch `Image`-Objekte gibt. Die Liste mit den Namen würde man am besten auch gleich zusammen mit den Bilddateien selbst erstellen, statt dort Informationen noch mal manuell zu wiederholen.
`Process` ist eine ganz komische Klasse. Da scheint die `__init__()` die komplette Arbeit zu machen und das Ergebnis wird zwar formal an einen sinnfreien/nichtssagenden Namen (`a`) gebunden, aber damit wird dann nichts gemacht. Eine `__init__()` sollte ein benutzbares Objekt erstellt und dann zum Aufrufer zurückkehren, damit der etwas mit dem Objekt machen kann. Wenn man das nicht hat, dann deutet das darauf hin, das die `__init__()` eigentlich gar nicht in eine Klasse gehört, sondern eine Funktion sein sollte. Oder ein Teil davon eine Methode auf dem Objekt, die dann später, nach dem Erstellen aufgerufen wird.
`i` und dann noch in einer Schleife ist ein schlechter Name für alles das keine ganze Zahl ist. Das sollte hier wohl `filename` oder `image_filename` heissen.
`images` wird unnötigerweise an das Objekt gebunden.
`line_count` ist eher etwas lokales von `start()`. Wenn man das in der `__init__()` setzt, dann muss man darauf vertrauen das der Leser, und das ist man als Autor letztlich ja auch selber, immer im Hinterkopf hat, dass das zwar hier nur einmal initiatialisiert wird, aber in der `start()` jeder Prozess seine eigene Kopie davon verändern wird.
Wenn man `line_count` in die `start()` verschiebt fällt dann auch gleich viel leichter auf, dass `line_count` immer den gleichen Werteverlauf hat, wie dass unbenutzte (und schlecht benannte) `y1`. Also kann man einfach `y1` in `line_count` umbenennen und kann sich das manuelle hochzählen sparen.
In der `__init__()` sind `t1` und `t2` eigentlich lokal und gehören nicht an das Objekt gebunden.
Zwischenstand: Das `Process` eine Klasse ist, hängt mittlerweile nur noch an *einem* Attribut: `self.total_lines`. Die `start()`-Methode greift da nur lesend drauf zu, das heisst man könnte den Wert auch einfach als Argument übergeben, und schon verschwindet die `Process`-Klasse, weil es keinen Zustand mehr gibt, den sie kapseln würde.
Die `map()`-Methode ist hier ja irgendwie falsch, weil Dich das Ergebnis überhaupt gar nicht interessiert.
Zwischenstand:
Code: Alles auswählen
#!/usr/bin/env python3
import concurrent.futures
import time
from PIL import Image
def load_image_height(filename):
_width, height = Image.open(filename).size
return height
def process_image(filename, total_lines):
for line_count in range(1, load_image_height(filename) + 1):
time.sleep(0.005)
progress = int(line_count / total_lines * 100)
print(progress)
#
# Processing things here.
#
def process_images(filenames):
total_lines = sum(map(load_image_height, filenames))
start_time = time.perf_counter()
with concurrent.futures.ThreadPoolExecutor() as executor:
for filename in filenames:
executor.submit(process_image, [filename, total_lines])
end_time = time.perf_counter()
print(f"Finished in {end_time - start_time} seconds")
def main():
image = Image.new("RGB", (1000, 1000), (255, 255, 255))
filenames = []
for name in ["a", "b", "c", "d"]:
filename = f"{name}.png"
image.save(filename)
filenames.append(filename)
process_images(filenames)
if __name__ == "__main__":
main()
Hier gibt ja jede Ausführung von `process_image()` (ehemals `Process.start()`) den eigenen Fortschritt bezogen auf die Gesamtzahl der Pixelzeilen aus. Aber in einem eigenen Prozess. Das Problem was ich sehe, ist das die `futures`-API es nicht vorsieht, dass die einzelnen Aufgaben während der Abarbeitung mit dem Auftraggeber kommunizieren. Und ich würde das da auch nicht an der API vorbei rein hacken wollen. Wenn Du also sowieso sicher bist, dass Du Prozesse brauchst, würde ich hier `multiprocessing` verwenden, denn das hat `Queue`-Objekte um mit dem Auftraggeber zu kommunizieren.