# HG changeset patch # User prymula # Date 1695328334 -7200 # Node ID dcd6105856106c253c6594c1b7274737a3c892bf INIT diff -r 000000000000 -r dcd610585610 bandm3u/bandm3u.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bandm3u/bandm3u.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# bandm3u- skrypt do tworzenia playlist m3u +# 16-02-2023 +# Edytor: Geany +# (c) by P.R.P +# Mój pierwszy skrypt stworzony w Windows 11 + +import os + +dir = os.getcwd() + +data_files = [] +data_out = [] +extension = ['mp3', 'ogg', 'wav'] + +def check(string, sub_str): + if (string.find(sub_str) == -1): + return False + else: + return True + +for data in os.listdir(dir): + for e in extension: + if check(data, e): + data_files += [os.path.basename(data)] + +#print (data_files) +for n in range(100): + for f in data_files: + if n > 9: + nn = str(n)+" " + else: + nn = "0"+str(n)+" " + # sprawdza liczbę porządkową + if check(f, nn): + data_out.insert(n, f) + + +c = '\\' + +# indeksy wysępowania znaku slash/unslash w łańcuchu +slash = [pos for pos, char in enumerate(dir) if char == c] +# zdejmuje ostatni indeks +last_slash = slash.pop() + + +f=open(dir[last_slash+1:]+".m3u","w+") # file name and mode +for x in range(0, len(data_out)): + f.writelines(data_out[x]) + f.writelines("\r\n") +f.close() + +print (dir) +print (data_out) diff -r 000000000000 -r dcd610585610 bubblesort/bubblesort-recursive.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bubblesort/bubblesort-recursive.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +tab = [3, 10, 1, 4, 5, 9, 8, 20, 11, 15, 12, 17, 18] + +def func1(i): + if i == 0: + return + else: + func2(len(tab) - i - 1) + #print("i: "+str(i)) + func1(i - 1) + +def func2(j): + if j < 0: + return + else: + if (tab[j] > tab[j + 1]): + tab[j], tab[j+ 1] = tab[j + 1], tab[j] + #print ("j:"+str(j)) + func2(j - 1) + +func1(len(tab)) +print(tab) diff -r 000000000000 -r dcd610585610 bubblesort/bubblesort.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bubblesort/bubblesort.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +tab = [3, 10, 1, 4, 5, 9, 8] + +n = len(tab) + +for i in range(0, n - 1): + for j in range(0, n - i - 1): + print ("j: "+str(j)) + if (tab[j] > tab[j + 1]): + tab[j], tab[j+ 1] = tab[j + 1], tab[j] + +print(tab) diff -r 000000000000 -r dcd610585610 bubblesort/test.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bubblesort/test.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +""" +for i int range(0,5): + print (i) +""" + +def func(i): + if i == 0: + return + print(i) + func(i-1) + +func(5) + diff -r 000000000000 -r dcd610585610 cover2musc/chanelog.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cover2musc/chanelog.txt Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,31 @@ +210512-4 - kolejna zmina numeru wersji, poprawki w makefile oraz przystosowanie do debianizowania +0.1-3 - zmiana numeru wersji i przeniesienie wszystkiego do /usr +20210512-3 - poprawiłem przyciski Ok w oknach dialogowych postępu, w ten sposób że pojawiają się dopiero po zakończonej operacji +20210512-2 - w pełni sprawne ProgrssBar dla usuwanych i dodawanych okładek, jedynie pozostaje wyregulować częstotlwość odpytywania funkcji callback. szczególne dla usuwania okladek +20210512-1 - przeniesienie funkcji usuwających okładkę do klasy DialogDeleteProgress, czyczenie kodu +20210512 - nieźle dzialająca klasa ProgressBar, jeszcze korzysta z funkcji poza klasowych +20210511 - przeniosłem część kodu odpowiedzialnego za usuwanie poza klase główną i przeniosłem wywołanie usuwania okładek z Okna Dialogowego +20210507-2 - nie udane próby dodania progress bar dla usuwanych okładek - okienko z postępem wyświetla się dopiero po usunięciu okładek +20210507-1 detekcja na postwaie 'magic number' zaimplementowana, jednak może okazać się nie wystarczająca dla plków MP3 (ID3v2.3) +20210507 - pierwsze nieśmiale próby detekcji pliku muzycznego innego niż rozszeżenie pliku +20210505-1 - usunięcie zbędnych atrybutów dla tagów okladki dla plików OPUS, zmiany w teksie pomocy +20210505 - dodano możliwość usuwania okładek z kontenera OGG z zachowaniem pozostalych tagów +20210504-1 - dla kontenera OGG jest teraz możliwe kasowanie plików o różnych kodekach +20210504 - dla kontenera OGG możliwy jest teraz wybór mieszany. +20210503-3 - dodałem obsługę plików OPUS, wyświetlają się zarówno w eksploatorze jak i odtwarzaczu +20210503-2 - dodałem dodawanie i usuwanie okladek dla formatu Ogg FLAC (OGA), jednak zmian nie widać w eksploatorze plików, tylko w odtwarzaczu graficznym +20210503-1 - powiększono okno na szerokość, oraz ustabilizowano przyciski +20210502-4 - dodano możliwość usuwania okładek z plików OGG, jednak kasowane są również pozostałe tagi +20210502-3 - poprawki w menu +20210502-2 - dopisałem możliwość usuwania okładek z plików mp3, dla plików ogg, implementacji brak +20210502 - zmiana nazwy na cover2music, zwiększenie idioto odporności +20210430e - na powrót konwertuje, ale jedynie pliki ogg a nie mp3 +20210430d - dodałem obsługę mp3, jednak program przestał konwertować +20210430c - dodałem automatyczne suwaki +20210428 - po zaimportowaniu modułu 'python3-mutagen' możliwe jest już dodawanie obrazków do plików ogg +20210427h - dodałem ramki w oknach oraz kontrolkach, dodałem link do strony domowej w About +20210427g - poprawiłem błąd przy kolejnym wyborze obrazka (linie 447-451) +20210427f - wyświetlanie wybranych utworów w widżecie +20210427e - przepisanie image_new oraz image z zmiennej globalnej do klasy +20210427d - implementacja wyświetlanego zmiany obrazka +20210426 - rozpoczęcie projektu diff -r 000000000000 -r dcd610585610 cover2musc/cover2music --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cover2musc/cover2music Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,3 @@ +#!/bin/sh + +python3 /opt/pietraszczyk/cover2music/main.py diff -r 000000000000 -r dcd610585610 cover2musc/cover2music.desktop --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cover2musc/cover2music.desktop Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,15 @@ +#!/usr/bin/env xdg-open + +[Desktop Entry] +Encoding=UTF-8 +Type=Application +Categories=AudioVideo;Audio; + +Name=Cover2Music + +Exec=python3 /usr/bin/cover2music.py +#Exec=gnome-terminal -e "bash -c 'python3 /usr/bin/cover2music.py;$SHELL'" +Terminal=false +Icon=/usr/share/pixmaps/cover2music.png + +Name[pl_PL]=Cover2Music diff -r 000000000000 -r dcd610585610 cover2musc/cover2music.ico Binary file cover2musc/cover2music.ico has changed diff -r 000000000000 -r dcd610585610 cover2musc/cover2music.png Binary file cover2musc/cover2music.png has changed diff -r 000000000000 -r dcd610585610 cover2musc/cover2music.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cover2musc/cover2music.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,835 @@ +#!/usr/bin/env python3 +# +# cover2music - appka do przypisywania obrazków płyt plikom ogg, oga, opus oraz mp3 +# program: Przemysław R. Pietraszczyk +# rozpoczęto: 26-04-2021 +# licencja: GPL +# napisano w edytorze: Atom + +import gi, os +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GdkPixbuf, GLib + +import base64 +from mutagen.mp3 import MP3 +from mutagen.id3 import ID3, APIC, error +from mutagen.oggvorbis import OggVorbis +from mutagen.oggflac import OggFLAC +from mutagen.oggopus import OggOpus +from mutagen.flac import Picture +import mutagen + +file_snd_selected=[] +file_img_selected="" + +#snd_selected=[] +#COVER ="cover2music.png" +COVER ="/usr/share/pixmaps/cover2music.png" +snd_extension="ogg" +img_extension="png" # dla przykladu + + +UI_INFO = """ + + + + + + + + + + + + + + + + + + + + + + + + + +""" + + +class FileChooserIMG(Gtk.Window): + def __init__(self): + Gtk.Window.__init__(self, title="Wybór plików graficznych") + global file_img_selected + + dialog = Gtk.FileChooserDialog(title="Please choose a file", parent=self, action=Gtk.FileChooserAction.OPEN) + dialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, + ) + + self.add_filters(dialog) + + response = dialog.run() + if response == Gtk.ResponseType.OK: + print("Open clicked") + print("File selected: " + dialog.get_filename()) + file_img_selected=dialog.get_filename() + elif response == Gtk.ResponseType.CANCEL: + print("Cancel clicked") + + dialog.destroy() + + def add_filters(self, dialog): + filter_jpeg = Gtk.FileFilter() + filter_jpeg.set_name("Pliki JPEG") + filter_jpeg.add_mime_type("image/jpeg") + dialog.add_filter(filter_jpeg) + + filter_png = Gtk.FileFilter() + filter_png.set_name("Pliki PNG") + filter_png.add_mime_type("image/png") + dialog.add_filter(filter_png) + +class FileChooserOgg(Gtk.Window): + def __init__(self): + Gtk.Window.__init__(self, title="Wybór plików dzwiękowych") + global file_snd_selected + dialog = Gtk.FileChooserDialog(title="Please choose a file", parent=self, action=Gtk.FileChooserAction.OPEN) + dialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, + ) + + self.add_filters(dialog) + dialog.set_select_multiple(True) + response = dialog.run() + if response == Gtk.ResponseType.OK: + print("Open clicked") + file_snd_selected=dialog.get_filenames() + + elif response == Gtk.ResponseType.CANCEL: + print("Cancel clicked") + + dialog.destroy() + + def add_filters(self, dialog): + + filter_ogg = Gtk.FileFilter() + filter_ogg.set_name("Pliki OGG") + filter_ogg.add_mime_type("audio/ogg") + dialog.add_filter(filter_ogg) + + filter_mp3 = Gtk.FileFilter() + filter_mp3.set_name("Pliki MP3") + filter_mp3.add_mime_type("audio/mp3") + dialog.add_filter(filter_mp3) + + +class DialogDeleteProgress(Gtk.Dialog): + def __init__(self,parent): + + Gtk.Dialog.__init__(self, title="Postęp", transient_for=parent, flags=0) + self.current_snd=0 + self.props.border_width = 20 + #self.add_buttons( + # Gtk.STOCK_OK, Gtk.ResponseType.OK + #) + self.progressbar = Gtk.ProgressBar() + self.set_default_size(150, 100) + box = self.get_content_area() + + label = Gtk.Label(label="Usuwam okladki z plików\n") + + box.add(label) + box.add(self.progressbar) + self.divider_next=1/len(file_snd_selected) + self.timeout_id = GLib.timeout_add(300, self.on_timeout, len(file_snd_selected)) + self.activity_mode = False + self.show_all() + + def on_timeout(self, user_data): + + #jesli przepytano wszyskie pliki wówczas koniec + if self.current_snd == user_data: + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + self.show_all() + return False + + if self.activity_mode: + self.progressbar.pulse() + else: + new_value = self.progressbar.get_fraction() + self.divider_next + + #if new_value > 1: + # new_value = 0 + + self.progressbar.set_fraction(new_value) + snd=file_snd_selected[self.current_snd] + self.current_snd = self.current_snd +1 + print ("file selected: ",snd) + extension = os.path.splitext(snd) + snd_extension=extension[1].replace(".", "") + print ("Rozszerzenie pliku dzwiekowego: ", snd_extension) + + type=self.check_file_type(snd) + if type == "mp3": + self.remove_cover_mp3_generic(snd) + elif type == "ogg": + self.remove_cover_ogg_generic(snd) + elif type == "oga": + self.remove_cover_oga_generic(snd) + elif type == "opus": + self.remove_cover_opus_generic(snd) + + # As this is a timeout function, return True so that it + # continues to get called + return True + + + def check_file_type(self,snd): + + magic_numbers = {'ID3v2': bytes([0x49, 0x44, 0x33]), + 'ID3v1': bytes([0xFF, 0xF2]), + 'ID3': bytes([0xFF, 0xF3]), + 'withoutID': bytes([0xFF, 0xFB]), + 'ogg': bytes([0x4F, 0x67, 0x67, 0x53])} + max_read_size = max(len(m) for m in magic_numbers.values()) + + with open(snd, 'rb') as fd: + file_head = fd.read() + #print(file_head) + + if file_head.startswith(magic_numbers['ID3v2']): + print ("Plik MP3- z ID3v2") + return "mp3" + elif file_head.startswith(magic_numbers['ID3v1']): + print ("Plik MP3- z ID3v1") + return "mp3" + elif file_head.startswith(magic_numbers['ID3']): + print ("Plik MP3- z ID3") + return "mp3" + elif file_head.startswith(magic_numbers['withoutID']): + print ("Plik MP3 bez ID3") + return "mp3" + elif file_head.startswith(magic_numbers['ogg']): + print("Kontener OGG") + ogg_head=mutagen.File(snd).pprint()[0:20] + + if 'opus' in ogg_head.lower(): + return "opus" + elif 'flac' in ogg_head.lower(): + return "oga" + elif 'vorbis'in ogg_head.lower(): + return "ogg" + + + + + def remove_cover_mp3_generic(self,snd): + + file = ID3(snd) # Load the file + file.delall("APIC") # Delete every APIC tag (Cover art) + file.save() # Save the file + + def remove_cover_ogg_generic(self,snd): + + file = OggVorbis(snd) + try: + artist=file["artist"] + except KeyError: + artist="" + try: + title=file["title"] + except KeyError: + title="" + try: + year=file["year_of_relase"] + except KeyError: + year="" + try: + album=file["album"] + except KeyError: + album="" + try: + genre=file["genre"] + except KeyError: + genre="" + + file.delete() + file["year_of_relase"]=year + file["artist"]=artist + file["title"]=title + file["album"]=album + file["genre"]=genre + file.save() + + def remove_cover_oga_generic(self,snd): + + file = OggFLAC(snd) + try: + artist=file["artist"] + except KeyError: + artist="" + try: + title=file["title"] + except KeyError: + title="" + try: + year=file["year_of_relase"] + except KeyError: + year="" + try: + album=file["album"] + except KeyError: + album="" + try: + genre=file["genre"] + except KeyError: + genre="" + + file.delete() + file["year_of_relase"]=year + file["artist"]=artist + file["title"]=title + file["album"]=album + file["genre"]=genre + file.save() + + def remove_cover_opus_generic(self,snd): + + file = OggOpus(snd) + #year=file["YEAR_OF_RELEASE"] + try: + artist=file["artist"] + except KeyError: + artist="" + try: + title=file["title"] + except KeyError: + title="" + try: + year=file["year_of_relase"] + except KeyError: + year="" + try: + album=file["album"] + except KeyError: + album="" + try: + genre=file["genre"] + except KeyError: + genre="" + + file.delete() + file["year_of_relase"]=year + file["artist"]=artist + file["title"]=title + file["album"]=album + file["genre"]=genre + file.save() + + +class DialogAddCoverProgress(Gtk.Dialog): + def __init__(self,parent): + + Gtk.Dialog.__init__(self, title="Postęp", transient_for=parent, flags=0) + self.current_snd=0 + self.props.border_width = 20 + #self.add_buttons( + # Gtk.STOCK_OK, Gtk.ResponseType.OK + #) + self.progressbar = Gtk.ProgressBar() + self.set_default_size(150, 100) + box = self.get_content_area() + + label = Gtk.Label(label="Dodaję okladkę do plików\n") + + box.add(label) + box.add(self.progressbar) + + self.divider_next=1/len(file_snd_selected) + self.timeout_id = GLib.timeout_add(300, self.on_timeout, len(file_snd_selected)) + self.activity_mode = False + self.show_all() + + def on_timeout(self, user_data): + global img_extension + #jesli przepytano wszyskie pliki wówczas koniec + if self.current_snd == user_data: + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + self.show_all() + return False + + if self.activity_mode: + self.progressbar.pulse() + else: + new_value = self.progressbar.get_fraction() + self.divider_next + + #if new_value > 1: + # new_value = 0 + + self.progressbar.set_fraction(new_value) + snd=file_snd_selected[self.current_snd] + self.current_snd = self.current_snd +1 + print ("file selected: ",snd) + extension = os.path.splitext(snd) + snd_extension=extension[1].replace(".", "") + print ("Rozszerzenie pliku dzwiekowego: ", snd_extension) + + + #print ("file selected: ",snd) + #extension = os.path.splitext(snd) + #snd_extension=extension[1].replace(".", "") + #print ("Rozszerzenie pliku dzwiekowego: ", snd_extension) + + if snd_extension.lower() == "ogg": + + file_ = OggVorbis(snd) + print("IMAGE FILE: ",file_img_selected) + with open(file_img_selected, "rb") as h: + data = h.read() + + img_extension=img_extension.lower() + picture = Picture() + picture.data = data + picture.type = 17 + picture.desc = u"Picture set by cover2music" + picture.mime = u"image/"+img_extension + picture.width = 100 + picture.height = 100 + picture.depth = 24 + + picture_data = picture.write() + encoded_data = base64.b64encode(picture_data) + vcomment_value = encoded_data.decode("ascii") + + file_["metadata_block_picture"] = [vcomment_value] + file_.save() + + elif snd_extension.lower() == "oga": + + #for snd in file_snd_selected: + file_ = OggFLAC(snd) + print("IMAGE FILE: ",file_img_selected) + with open(file_img_selected, "rb") as h: + data = h.read() + + img_extension=img_extension.lower() + picture = Picture() + picture.data = data + picture.type = 17 # 0x7f + picture.desc = u"Picture set by cover2music" + picture.mime = u"image/"+img_extension + picture.width = 100 + picture.height = 100 + picture.depth = 24 + + picture_data = picture.write() + encoded_data = base64.b64encode(picture_data) + vcomment_value = encoded_data.decode("ascii") + + file_["metadata_block_picture"] = [vcomment_value] + file_.save() + + + elif snd_extension.lower() == "opus": + + #for snd in file_snd_selected: + file_ = OggOpus(snd) + print("IMAGE FILE: ",file_img_selected) + with open(file_img_selected, "rb") as h: + data = h.read() + + img_extension=img_extension.lower() + picture = Picture() + picture.data = data + picture.type = 17 # 0x7f + picture.desc = u"Picture set by cover2music" + picture.mime = u"image/"+img_extension + #picture.width = 100 + #picture.height = 100 + #picture.depth = 24 + + picture_data = picture.write() + encoded_data = base64.b64encode(picture_data) + vcomment_value = encoded_data.decode("ascii") + + file_["metadata_block_picture"] = [vcomment_value] + file_.save() + + elif snd_extension.lower() == "mp3": + #for snd in file_snd_selected: + print ("Dodaje do mp3: ",snd) + print ("file_img_selected: ", file_img_selected) + file = MP3(snd, ID3=ID3) + + try: + file.add_tags() + except error: + pass + + file.tags.add(APIC(mime="image/"+img_extension,type=3,desc=u'Cove by cover2music',data=open(file_img_selected,'rb').read())) + file.save() # save the current changes + + else: + print ("źle") + + # As this is a timeout function, return True so that it + # continues to get called + return True + + + +class DialogHelp(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="Pomoc", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + + self.set_default_size(150, 100) + txt="-jednak samo załadownie obrazka do pliku nie gwarantuje, że zmieni się ikona w menedżerze plików.\n W tym względzie są przyjazne menedżery takie jak PCManFM, jak i nieprzyjazne jak Nemo.\n Jednak w graficznym odtwarzaczu, załadowany obrazek powinien pojawić się zawsze\n\n" + label1 = Gtk.Label(label="Aplikacja jest w stanie dodać okładkę do prawie wszystkich plików MP3, oraz do plików OGG (Vorbis),\n OGA (OGG FLAC) oraz OPUS. Możliwe jest również usuwanie okładek z pozostawieniem pozostałych tagów.\n Pliki można zmieniać pojedynczo jak i grupowo. Okładki odczytywane są w formatach JPEG oraz PNG.\n Należy zadbać wcześniej aby obrazy okładek nie były zbyt duże, optymalna wielkość to 384x384 px.\n\n") + label2 = Gtk.Label(label=" Uwagi:\n-format JPEG w plikach OPUS nie jest wspierany \n-mieszana kolekcja plików zakodowanych rożnymi kodekami w kontenerach OGG jest co prawda możliwa,\n jednak skrypt może zachowywać się niestabilnie\n"+txt) + box = self.get_content_area() + box.add(label1) + box.add(label2) + self.show_all() + +class DialogAbout(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="O Programie", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + + self.set_default_size(150, 100) + + label = Gtk.Label(label="\tAplikacja napisana na podstawie:\n\nhttps://python-gtk-3-tutorial.readthedocs.io/\n\n\t\t\tWersja: 0.1-3\n\n\t\tPrzemysław R. Pietraszczyk\n\n\t\t\tKwiecień/Maj 2021\n\t\t\tLicencja: GPL\n\n") + #label3 = Gtk.Label(label="-jednak samo załadownie obrazka do pliku nie gwarantuje, że zmieni się ikona w menedżerze plików.\n W tym względzie są przyjazne menedżery takie jak PCManFM, jak i nie przyjazne jak Nemo.\n Jednak w graficznym odtwarzaczu, załadowany obrazek powinien pojawić się zawsze.\n\n") + + box = self.get_content_area() + box.add(label) + + button = Gtk.LinkButton("http://www.prp.xlx.pl", label="Strona Domowa") + box.add(button) + + self.show_all() + +class DialogNoSelectIMG(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="Błąd", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + + self.set_default_size(150, 100) + + label = Gtk.Label(label="Nie wybrano żadnego pliku z muzyką!\n") + + box = self.get_content_area() + box.add(label) + + self.show_all() + +def get_resource_path(rel_path): + dir_of_py_file = os.path.dirname(__file__) + rel_path_to_resource = os.path.join(dir_of_py_file, rel_path) + abs_path_to_resource = os.path.abspath(rel_path_to_resource) + return abs_path_to_resource + + +grid = Gtk.Grid() + + +class MyWindow(Gtk.Window): + + def __init__(self): + super().__init__() + + self.init_ui() + + + def init_ui(self): + global grid, file_img_selected, snd_extension + self.image_new=None + #grid = Gtk.Grid() + self.props.border_width = 20 + #self.img_extension="png" # dla przykladu + #snd_extension="ogg" + + self.add(grid) + + grid.set_row_spacing(10) + grid.set_column_spacing(10) + grid.set_column_homogeneous(True) # rozszerza kontrolke z utworami na resztę okna + + action_group = Gtk.ActionGroup(name="my_actions") + + self.add_file_menu_actions(action_group) + self.add_info_menu_actions(action_group) + self.add_choices_menu_actions(action_group) + + uimanager = self.create_ui_manager() + uimanager.insert_action_group(action_group) + + menubar = uimanager.get_widget("/MenuBar") + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + box.pack_start(menubar, False, False, 0) + + grid.attach(box, 0, 0, 2, 1) + file_img_selected=get_resource_path(COVER) + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_selected, + width=100, + height=100, + preserve_aspect_ratio=True) + + self.image = Gtk.Image.new_from_pixbuf(pixbuf) + grid.attach(self.image,0,1,1,1) + + quitBtn = Gtk.Button(label="Wykonaj !") + quitBtn.set_size_request(60, 30) + + quitBtn.connect("clicked", self.on_button_clicked) + + grid.attach(quitBtn, 0, 2, 1, 1) # lewo, top wielkosc, wysokosc + + self.liststore = Gtk.ListStore(str, str) + self.treeview = Gtk.TreeView(model=self.liststore) + + self.renderer_text = Gtk.CellRendererText() + self.column_text = Gtk.TreeViewColumn("Wybrane Utwory", self.renderer_text, text=0) + self.treeview.append_column(self.column_text) + + self.scrolled_window = Gtk.ScrolledWindow () + self.scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + + self.scrolled_window.add (self.treeview) + grid.attach_next_to(self.scrolled_window, self.image ,Gtk.PositionType.RIGHT,6,2) + + self.set_border_width(10) + self.set_title("Cover2Music") + self.set_default_size(650, 180) + self.connect("destroy", Gtk.main_quit) + + self.set_icon_from_file(get_resource_path(COVER)) + + def on_button_clicked(self, widget): + global file_img_selected, snd_extension + + if len(file_snd_selected)<1: + print("nie zaznaczyleś żadnego pliku z muzyką !") + dialog = DialogNoSelectIMG(self) + response = dialog.run() + dialog.destroy() + return + + + progress =DialogAddCoverProgress(self) + response = progress.run() + progress.destroy() + + + def add_info_menu_actions(self, action_group): + action_info_menu = Gtk.Action(name="InfoMenu", label="Info") + action_group.add_action(action_info_menu) + + action_new = Gtk.Action( + name="Help", + label="Pomoc", + tooltip="Create a new file", + ) + action_new.connect("activate", self.on_menu_help) + action_group.add_action_with_accel(action_new, None) + + action_new = Gtk.Action( + name="About", + label="O Programie", + tooltip="Create a new file", + ) + action_new.connect("activate", self.on_menu_about) + action_group.add_action_with_accel(action_new, None) + + def add_file_menu_actions(self, action_group): + action_filemenu = Gtk.Action(name="FileMenu", label="Plik") + action_group.add_action(action_filemenu) + + action_fileloadmenu = Gtk.Action(name="FileLoad", stock_id=Gtk.STOCK_NEW) + action_group.add_action(action_fileloadmenu) + + action_new = Gtk.Action( + name="LoadImg", + label="Załaduj okladkę", + tooltip="Wybór okladki", + ) + action_new.connect("activate", self.on_menu_file_load_img_generic) + action_group.add_action_with_accel(action_new, None) + + + action_new = Gtk.Action( + name="LoadOgg", + label="Załaduj muzykę", + tooltip="Wybór muzyki", + ) + action_new.connect("activate", self.on_menu_file_load_ogg_generic) + action_group.add_action_with_accel(action_new, None) + + action_fileloadmenu = Gtk.Action(name="FileDelete", label="Usuń", stock_id=None) + action_group.add_action(action_fileloadmenu) + + action_new = Gtk.Action( + name="RemoveCover", + label="Usuwa okladkę", + tooltip="Usuwa okladkę z pliku", + ) + action_new.connect("activate", self.on_menu_file_erase_cover_all) + action_group.add_action_with_accel(action_new, None) + + + action_filequit = Gtk.Action(name="FileQuit", stock_id=Gtk.STOCK_QUIT) + action_filequit.connect("activate", self.on_menu_file_quit) + action_group.add_action(action_filequit) + + def add_choices_menu_actions(self, action_group): + action_group.add_action(Gtk.Action(name="ChoicesMenu", label="Opcje")) + + three = Gtk.ToggleAction(name="ChoiceTree", label="Pozwolenie") + three.connect("toggled", self.on_menu_choices_toggled) + action_group.add_action(three) + + # tworzy menu bar + def create_ui_manager(self): + uimanager = Gtk.UIManager() + + # Throws exception if something went wrong + uimanager.add_ui_from_string(UI_INFO) + + # Add the accelerator group to the toplevel window + accelgroup = uimanager.get_accel_group() + self.add_accel_group(accelgroup) + return uimanager + + def on_menu_help(self, widget): + dialog = DialogHelp(self) + response = dialog.run() + + dialog.destroy() + + def on_menu_about(self, widget): + dialog = DialogAbout(self) + response = dialog.run() + + dialog.destroy() + + + def on_menu_file_load_img_generic(self, widget): + global grid, file_img_selected, img_extension + + print("A File|New menu item was selected.") + file=FileChooserIMG() + print ("file:"+str(file_img_selected)) + pixbuf_new = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_selected, + width=100, + height=100, + preserve_aspect_ratio=True) + + if self.image != None: + grid.remove(self.image) + self.image=None + else: + grid.remove(self.image_new) + + + # TODO - pozbyć się self.image_new na rzecz self.image + self.image_new = Gtk.Image.new_from_pixbuf(pixbuf_new) + grid.attach_next_to(self.image_new, self.scrolled_window,Gtk.PositionType.LEFT,1,1) + + self.show_all() + + extension = os.path.splitext(file_img_selected) + #self.img_extension=extension[1].replace(".", "") # tupla (0) wskazuje na nazwe pliku ! + img_extension=extension[1].replace(".", "") # tupla (0) wskazuje na nazwe pliku ! + + + def on_menu_file_load_ogg_generic(self, widget): + global file_snd_selected + file=FileChooserOgg() + #grid.remove(self.treeview) + self.scrolled_window.remove(self.treeview) + grid.remove(self.scrolled_window) + self.liststore = Gtk.ListStore(str, str) + self.treeview.remove_column(self.column_text) + + print ("file selected: ",file_snd_selected) + + for o in file_snd_selected: + self.liststore.append([o,""]) + + self.scrolled_window = Gtk.ScrolledWindow () + self.scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + + self.treeview = Gtk.TreeView(model=self.liststore) + self.renderer_text = Gtk.CellRendererText() + self.column_text = Gtk.TreeViewColumn("Wybrane Utwory", self.renderer_text, text=0) + self.treeview.append_column(self.column_text) + + if self.image != None: + self.scrolled_window.add (self.treeview) + grid.attach_next_to(self.scrolled_window, self.image ,Gtk.PositionType.RIGHT,6,2) + else: + self.scrolled_window.add (self.treeview) + grid.attach_next_to(self.scrolled_window, self.image_new ,Gtk.PositionType.RIGHT,6,2) + + self.show_all() + """ + def close_progress(self, gparmstring): + self.progress.destroy() + """ + def on_menu_file_erase_cover_all(self, widget): + + if len(file_snd_selected)<1: + dialog = DialogNoSelectIMG(self) + response = dialog.run() + dialog.destroy() + return + + self.progress =DialogDeleteProgress(self) + response = self.progress.run() + self.progress.destroy() + + + def on_menu_file_quit(self, widget): + Gtk.main_quit() + + def on_menu_choices_toggled(self, widget): + if widget.get_active(): + print(widget.get_name() + " activated") + else: + print(widget.get_name() + " deactivated") + + def on_button_press_event(self, widget, event): + # Check if right mouse button was preseed + if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3: + self.popup.popup(None, None, None, None, event.button, event.time) + return True # event has been handled + + +win = MyWindow() +win.show_all() +Gtk.main() diff -r 000000000000 -r dcd610585610 cover2musc/makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cover2musc/makefile Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,18 @@ +# +prefix=/usr + +install: +# mkdir -pm -755 $(prefix) + mkdir -pm -755 $(prefix)/share/pixmaps + install -m 0755 cover2music.py $(prefix)/bin + install -m 0644 cover2music.png $(prefix)/share/pixmaps + install -m 0755 cover2music.desktop $(prefix)/share/applications + +.PHONY: install + +uninstall: + rm $(prefix)/bin/cover2music.py + rm $(prefix)/share/pixmaps/cover2music.png + rm $(prefix)/share/applications/cover2music.desktop + +.PHONY: uninstall diff -r 000000000000 -r dcd610585610 cover2musc/setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cover2musc/setup.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,29 @@ +#setup.py +from distutils.sysconfig import get_python_lib +from os.path import join +from glob import glob +from cx_Freeze import setup, Executable + +# Basically just copy all of the CEF distribution into the installer +# I have only tested this on windows +cefPath = join(get_python_lib(), "cefpython3") +CEF_INCLUDES = glob(join(cefPath, "*")) +#CEF_INCLUDES.remove(join(cefPath, "examples")) + +setup( + name = "cover2music", + version = "20210428", + options = { + "build_exe": { + 'packages': ["os","gi","gi.repository","base64","mutagen.oggvorbis","mutagen.flac","mutagen.mp3","mutagen.id3"], + 'include_files': CEF_INCLUDES + ['cover2music.png'], + 'include_files': CEF_INCLUDES, + + 'include_msvcr': True, + }}, + #executables = [Executable("main.py", base="Win32GUI")] + #executables = [Executable("main.py", base = "Win32GUI"), icon = "C:/Program Files/iNTMI/assets/images/programIcon.ico")] + executables = [Executable("main.py", base = "Win32GUI"), icon = "cover2music.ico")] + + +) diff -r 000000000000 -r dcd610585610 photocrop/DEBIAN/HOWTO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/DEBIAN/HOWTO Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,2 @@ +foramt zapisu nazwy pliku: + photocrop_0.221225-0~alpha.tar.gz diff -r 000000000000 -r dcd610585610 photocrop/DEBIAN/debian.changelog --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/DEBIAN/debian.changelog Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,5 @@ +photocrop (0.221225-4) unstable; urgency=low + + * Last release + + -- Przemysław R. Pietraszczyk Sat, 25 Dec 2022 08:31:41 +0200 diff -r 000000000000 -r dcd610585610 photocrop/DEBIAN/debian.control --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/DEBIAN/debian.control Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,13 @@ +Source: photocrop +Section: python +Priority: extra +Maintainer: Przemysław R. Pietraszczyk +Build-Depends: debhelper-compat (= 13), python3-all, python-setuptools, dh-python, python3-gi, python3-gi-cairo, gir1.2-gtk-3.0, fakeroot +Standards-Version: 0.230112-0~alpha +Homepage: http://pietraszczyk.vxm.pl/przycianie-zdjec + + +Package: photocrop +Architecture: all +Depends: ${misc:Depends}, python3-all, python3-gi, python3-gi-cairo, gir1.2-gtk-3.0 +Description: An application for cropping photos to popular paper sizes. diff -r 000000000000 -r dcd610585610 photocrop/DEBIAN/debian.rules --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/DEBIAN/debian.rules Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,29 @@ +#!/usr/bin/make -f + +export DH_VERBOSE = 1 +#export PYBUILD_NAME = photocrop +#export PYBUILD_SYSTEM = custom + +clean: + @ + +build: + @ + +binary: + mkdir -pm 0755 debian/photocrop + mkdir -pm 0755 debian/photocrop/usr/bin + mkdir -pm 0755 debian/photocrop/usr/share + mkdir -pm 0755 debian/photocrop/usr/share/photocrop + mkdir -pm 0755 debian/photocrop/usr/share/applications + cp src/photocrop.py debian/photocrop/usr/bin + cp data/clip.png debian/photocrop/usr/share/photocrop + cp data/IMG_6854.JPG debian/photocrop/usr/share/photocrop + cp data/photocrop.desktop debian/photocrop/usr/share/applications + dh_gencontrol + dh_builddeb +# dh $@ --with python3 --buildsystem=pybuild +# dh $@ --with python3 + +#override_dh_auto_configure: +# dh_auto_configure -- diff -r 000000000000 -r dcd610585610 photocrop/DEBIAN/format.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/DEBIAN/format.sh Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,1 @@ +exec /usr/lib/build/debtransform ./ ./photocrop.dsc ./exec diff -r 000000000000 -r dcd610585610 photocrop/DEBIAN/photocrop.dsc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/DEBIAN/photocrop.dsc Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,41 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +Format: 1.0 +Source: photocrop +Binary: photocrop +Architecture: all +Version: 0.230112-0~alpha +Maintainer: Przemysław R. Pietraszczyk +Homepage: http://pietraszczyk.vxm.pl +DEBTRANSFORM-TAR: debian.tar.gz +DEBTRANSFORM-TAR: photocrop_0.2301012-0~alpha.tar.gz + +Standards-Version: 0.2301012-0~alpha +Build-Depends: debhelper-compat (= 13), python3-all, python-setuptools, dh-python, python3-gi, python3-gi-cairo, gir1.2-gtk-3.0, fakeroot +Package-List: + photocrop deb x11 optional arch=all +Files: + b31649bc23b2b7bca9ab70ea3410711a 128820 photocrop_0.230112-0~alpha.tar.gz + 29e7e7e60bc81891e3da3871b9eeb549 764 photocrop-0.230112-0~alpha.debian.tar.xz + +-----BEGIN PGP SIGNATURE----- + +mQENBGEf/c4BCAC2d4ymW0pRZV36qLtlt/WGb83kos5UX5UbwvkQrbUjAbOPOY8w +DT3M1neYOAPZx38924aPTFKbZPcz+rK/7Wcv1kzgfux5zXQJTLeqpkhUYAgXUg2p +oK9ZXsai82fOicTrClOyJLLYQ8C1vj8yUh7e3ERljEyp5Nxg/lY92rwcZ4WYR193 +nGDInlDl5JlWUcLlk/RFnC5bB+T6ZZr5FBX/eDnKAPEl+N3MWpDs2JLDy7EUPhcG +U/60X0wuXHPTwMoNbB3ep/bWRxwEej0sFad5GXeCh7hKCroi/kLmLTDIxSD8lsRr +Y4H/8H2lBBsQq7bRL88N6ZDGCocPo11V83kFABEBAAG0NmhvbWU6cHJ6ZW0gT0JT +IFByb2plY3QgPGhvbWU6cHJ6ZW1AYnVpbGQub3BlbnN1c2Uub3JnPokBPgQTAQgA +KAUCYR/9zgIbAwUJBB6wAAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQcXEc +NPcNefFEAAf+Kg4cs/C99kEIMv/PO85VW+p8rcTwCViOFUX5WrBhwsRyPAKrJPPG +KD6PdnvoplcNQwhQ8nRsRiiaiUtPcj8Bvi6EdPgSgXXObKKJtCyaAa1rURdlb9iW +CfvXA6oW+PGlii9BMnXOAtkeFIzZooO9oXd+wez66P+4VWFiFzxanLfLn/hwbWxQ +68rlO3QNGbrMeODBgwh9s83XGmAg47mn2fldTAmTbJDf9tMi1LhX2MmLyN9wjqsd +4IZ9JMri2JTnIhYfxWdKT9gsu7q0g8LqonaXMvlOEyHFRF1YQ0i7G6fFbvDcKCZN +W1Eja55FdN7smv5Eck6UTIIYyWPuAv45uohGBBMRAgAGBQJhH/3PAAoJEDswEbdr +nWUj4M0AniJ389dyvUjjgS91AcOb08E4moV9AJ9WDL3/7GYYUL/Azw43QucWPnXr +uw== +=+lfH +-----END PGP SIGNATURE----- diff -r 000000000000 -r dcd610585610 photocrop/INSTALL --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/INSTALL Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,92 @@ +Windows + +Go to http://www.msys2.org/ and download the x86_64 installer + +Follow the instructions on the page for setting up the basic environment + +Run C:\msys64\mingw64.exe - a terminal window should pop up + +Execute pacman -Suy + +Execute pacman -S mingw-w64-x86_64-gtk3 mingw-w64-x86_64-python3 mingw-w64-x86_64-python3-gobject + + +Ubuntu/Debian + +Installing the system provided PyGObject: +Open a terminal + +Execute sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0 + +Installing from PyPI with pip: +Open a terminal and enter your virtual environment + +Execute sudo apt install libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev gir1.2-gtk-3.0 to install the build dependencies and GTK + +Execute pip3 install pycairo to build and install Pycairo + +Execute pip3 install PyGObject to build and install PyGObject + + + +Fedora + +Installing the system provided PyGObject: +Open a terminal + +Execute sudo dnf install python3-gobject gtk3 + +Open a terminal and enter your virtual environment + +Execute sudo dnf install gcc gobject-introspection-devel cairo-gobject-devel pkg-config python3-devel gtk3 to install the build dependencies and GTK + +Execute pip3 install pycairo to build and install Pycairo + +Execute pip3 install PyGObject to build and install PyGObject + + +Arch Linux + +Installing the system provided PyGObject: +Open a terminal + +Execute sudo pacman -S python-gobject gtk3 + +Installing from PyPI with pip: +Open a terminal and enter your virtual environment + +Execute sudo pacman -S python cairo pkgconf gobject-introspection gtk3 to install the build dependencies and GTK + +Execute pip3 install pycairo to build and install Pycairo + +Execute pip3 install PyGObject to build and install PyGObject + + + + +openSUSE + +Installing the system provided PyGObject: +Open a terminal + +Execute sudo zypper install python3-gobject python3-gobject-Gdk typelib-1_0-Gtk-3_0 libgtk-3-0 + +Installing from PyPI with pip: +Open a terminal and enter your virtual environment + +Execute sudo zypper install cairo-devel pkg-config python3-devel gcc gobject-introspection-devel to install the build dependencies and GTK + +Execute pip3 install pycairo to build and install Pycairo + +Execute pip3 install PyGObject to build and install PyGObject + + + + +macOS + +Go to https://brew.sh/ and install homebrew + +Open a terminal + +Execute brew install pygobject3 gtk+3 diff -r 000000000000 -r dcd610585610 photocrop/LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/LICENSE Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff -r 000000000000 -r dcd610585610 photocrop/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/Makefile Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,21 @@ + +prefix=/usr + +all: + +install: + mkdir -pm 0755 $(prefix)/share/photocrop + install -m 0755 src/photocrop.py $(prefix)/bin + install -m 0644 data/clip.png $(prefix)/share/photocrop + install -m 0644 data/IMG_6854.JPG $(prefix)/share/photocrop + install -m 0644 data/photocrop.desktop $(prefix)/share/applications + + +uninstall: + rm $(prefix)/share/photocrop/clip.png + rm $(prefix)/share/photocrop/IMG_6854.JPG + rmdir $(prefix)/share/photocrop/ + rm $(prefix)/bin/photocrop.py + rm $(prefix)/share/applications/photocrop.desktop + +.PHONY: all install uninstall diff -r 000000000000 -r dcd610585610 photocrop/Makefile.in.gz Binary file photocrop/Makefile.in.gz has changed diff -r 000000000000 -r dcd610585610 photocrop/RPM/photocrop.spec --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/RPM/photocrop.spec Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,71 @@ +# +# spec file for package photocrop +# +# Copyright (c) 2020 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + +%define unmangled_version 0.230112-0~alpha +Name: photocrop +Version: 0.230112 +Release: 0~alpha +Summary: An application for cropping photos to popular paper sizes. +License: GPL +URL: https://pietraszczyk.vxm.pl/przycinanie-zdjec/ +Source0: %{name}-%{unmangled_version}.tar.gz +%if 0%{?suse_version}>=150000 +BuildRequires: python3, python3-gobject, python3-gobject-Gdk, typelib-1_0-Gtk-3_0, libgtk-3-0 +Requires: python3, python3-gobject, python3-gobject-Gdk, typelib-1_0-Gtk-3_0, libgtk-3-0 +%endif +%if 0%{?fedora}>=35 +BuildRequires: python3, python3-gobject, gtk3 +Requires: python3, python3-gobject, gtk3 +%endif +BuildArch: noarch + +%description + + +%prep +%setup -n %{name}-%{unmangled_version} -n %{name}-%{unmangled_version} + +%build + +%install + +mkdir -p %{buildroot}/usr +mkdir -p %{buildroot}/usr/share +mkdir -p %{buildroot}/usr/share/photocrop +mkdir -p %{buildroot}/usr/share/applications +mkdir -p %{buildroot}/usr/bin + +install -m 0755 src/photocrop.py %{buildroot}/usr/bin +install -m 0644 data/clip.png %{buildroot}/usr/share/photocrop/clip.png +#install -m 0644 data/blank.png %{buildroot}/usr/share/photocrop/blank.png +install -m 0644 data/IMG_6854.JPG %{buildroot}/usr/share/photocrop/IMG_6854.JPG +install -m 0644 data/photocrop.desktop %{buildroot}/usr/share/applications + + + +%files +%defattr(0755,root,root) +/usr/bin/photocrop.py +/usr/share/photocrop/ +%defattr(0644,root,root) +/usr/share/photocrop/clip.png +#/usr/share/photocrop/blank.png +/usr/share/photocrop/IMG_6854.JPG +/usr/share/applications/photocrop.desktop + +%changelog + diff -r 000000000000 -r dcd610585610 photocrop/bugs.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/bugs.txt Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,3 @@ +1. Losowe zniekształcenia na podglądzie po przycięciu, ale tylko na podglądzie, wydruk jest prawidłowy. +2. Źle współpracuje z grafiką utworzoną w Krita. +3. Przed wydrukiem należy spojrzeć w kreatorze wydruku na sugerowaną orientację zdjęcia. diff -r 000000000000 -r dcd610585610 photocrop/changelog --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/changelog Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,70 @@ +0.230112-0 - Poprawki odświeżania okna po przycięciu +0.230107-0 - Poprawiłem skalowanie dla średniego formatu. Wyszła na jaw zła współpraca z grafiką utworzoną w Krita ! +0.221229-0 - Interfejs po angielsku, poprawiłem skalowanie ramki dla małego i dużego formatu. +0.221225-0 - Korekta wielkości początkowej ramki +0.221224-3 - Poprawki dla konfiguracji ramki dla pozostałych formatów +0.221234-2 - Poprawiłem skalowanie dla zdjęć horyzontalnych +0.221224-1 - W zależności jaka była pierwotnie orientacja zdjęcia, po obrocie jest ono odpowiednio przycinane +0.221224-0 - Nie rozciąga już zdjęcia po przycięciu po obrocie na wertylkane +0.221223-0 - Poprawiłem odzyskiwanie ramki po obrocie, dodałem zapis przyciętej fotografii +0.221214-3 - Zrezygnowałem z ograniczeń ramki przy krawędziach zdjęcia gdyż źle oddziaływały na ramkę. Przywróciłem sposób z wyświetlaniem okna dialogowego po przekroczeniu krawędzi. +0.221214-2 - Poprawki drobnych błędów +0.221214-1 - Zwiększyłem rozmiar ramki po obrocie +0.221214-0 - Poprawiłem błąd powodujący wyłączenie się skryptu po przycięciu obrazka w dolnych partiach +0.221213-2 - Dodałem menu wraz z odnośnikiem z licencją (w końcu!). Poprawiłem informacje "O Programie" +0.221213-1 - Poprawki błędów związanych z literówkami dotyczącymi aspektu ramki +0.221213-0 - Poprawiłem odświeżanie kontrolki po obrocie - jednak dla obrazków wertykalnych zbyt niskie położenie ramki grozi krachem aplikacji +0.221212-1 - Poprawiłem przekształcanie ramki po obrocie +0.221212-0 - Okno zmienia już swoją wielkość po rotacji, lepsze dobór ramki po rotacji obrazu. +0.221211-5 - Prawidłowo obraca obrazek do wydruku +0.221211-4 - W celu okiełzania ramki należy operować wartościami w "self.border_properties" +0.221211-3 - Przycina obrócony obraz, jednak partaczy wydruk +0.221211-2 - Ramka już nie przyrasta vertykalnie +0.221211-0 - Próba implementacji obrotu ramki - do poprawki +0.221210-0 - Przepisanie rozmiaru ramki do słownika. Z sukcesem ! FIXME - zablokować możliwość powiększania ramki po dojściu z jednego z brzegów do konturu. W przeciwnym wypadku kłuci się to z sensownością aplikacji. +0.220317-0 - Dodałem odczyt plików TIFF, jednak aplikacja źle przycina – szare tło +0.220315-1 - Należało by aby aplikacja rozróżniała pochodzenie zdjęcia (lustrzanka, kompakt, smartphone)) w celu doboru współczynnika ramki +0.220314-0 - Drukuje prawidłowo zdjęcia zarówno wertykalne jak i horyzontalne, jednak przy tych ostatnich papier należy również umieścić horyzontalnie w drukarce ! +0.220308-0 - Dalsza korekta ustawień ramki oraz skalowania ramki dla rozmiaru A6 +0.220306-1 - Likwidacja podmenu otwarcia pliku, rozmiar okna dostosowuje się do położenia/rozmiaru zdjęcia. +0.220306-0 - Korekta ramki dla zdjęć horyzontalnych, korekta dotyczy również skalowania tych zdjęć. Jednak są sytuacje gdy dodawana jest ramka do zdjęć. +0.220217-0 - Teraz drukuje ! +0.220216-3 - Korekta przy współżędnych tworzenia ramki, potrzebna korekta ramki przy skalowaniu +0.220216-2 - Małe poprawki w 'Gtk.PaperSize.new.custom' +0.220216-1 - Próba wydruku na orginalnych wymiarach zaraz po przycięciu, nie wiem czy drukarka ruszy... +0.220216-0 - Podbicie nieco pikseli w wydruku, przejście z milimetrów na punkty przy wydruku +0.220214-1 - Przywrócenie poprzednich rozmiarów ramki +0.220214-0 - Rezygnacja ze stalej 0.075 na rzecz nie odejmowania 'crop_y' +0.220213-0 - Przypisanie 'self.pixbuf_tmp' wartości None, oraz zapobiegnięcie wyjścia ramki poza obrazek przy scrolowaniu kółkiem myszki. +0.220212-1 - Kalibracja wydruku +0.220212-0 - Dodałem ograniczenia dla ramki na krawędziach zdjęcia +0.220211-0 - Korekcja wspóczynników ramki dla rozmiaru 13x18 +0.220210-0 - Poprawiłem przywracanie zdjęcia w kontrolce dla 13x18 +0.220205-0 - Wstępne ustawienia dla papieru do wydruku – wszystkie formaty. +0.220204-1 - Przyczółek do korekty wydruku w rozmiarze 10x15 +0.220204-0 - Poprawiłem odświeżenie podglądu po przywróceniu, jednak dla formatu ‘13x18’ odświeżenie bywa problematyczne. Dopisałem opcje drukowania bezpośrednio z ‘prevew’. +0.220202-5 - Dopisałem ustawienia ramki dla pozostałych rozmiarów, uaktualniłem przypisanie rozmiaru obrazka w kontrolce po przywróceniu. +0.220202-4 - Zakomentowanie odjęcia stałej 0,075 od 'bw' - powód: wyjście poza obszar przy przycięciu w okolicach prawego skraju zdjęcia. +0.220202-3 - Optymalizacja kodu → set_ratio_picture_view() +0.220202-2 - Optymalizacja kodu → self.set_ratio_border_size() +0.220202-1 - Włącznie stałej 0,075 również do zmiennej ‘bw’ dla zachowania proporcji obrazu. +0.220202-0 - Ustawienie stałej 0.075 jedynie dla zmiennej ‘bh’, poprawka dla aktualizacji podglądu po przycięciu. +0.220201-2 - Zmniejszenie progresji ramki w funkcji ‘on_mouse_move_in_drawing_area() +0.220201-1 - Dopisanie stalych 0.075 do odjęcia ze źródla wzgldem ramki +0.220201-0 - Poprawiłem przycinanie dolnych partii zdjęcia po przycięciu. Jednak teraz pojawił się narzut po prawej stronie w wyniku. +0.229128-1 - Cyrkulacja ramki +0.220129-0 - Dodałem wyłączenie ramki po przycięciu, przywracanie fotografii do oryginału, po nieudanym przycięciu. Jednak skalowanie ramki nie jest idealne, przy pomniejszonym zaznaczeniu bywa że obcina dół. +0.220128-0 - Napisałem wycinanie brzegów, jednak wycina więcej nie wskazuje ramka ! Potrafi przyciąć nogi. +0.220125-2 - Nieudana propozycja kadrowania +0.220125-1 - Napisałem przycinane zdjęcia, źle kadruje zdjęcia +0.220125-0 - Prawidłowe wyliczenie proporcji ramki dla papieru A6 +0.220124-1 - Próba sformatowania ramki o proporcjach 3/2 dla wszystkich rozmiarów fotografii cyfrowych +0.220124-0 - Poprawiłem odświeżanie ramki po ponownym wczytaniu zdjęcia, skalowana ramka dla wszystkich rozmiarów, jeszcze nie dokładna +0.220123-2 - Dopisałem przesuwanie ramki za pomocą myszki +0.220123-1 - Ustawiłem prawidłowe skalowanie ramki dla rozmiarów 10z15 0raz A4 (13x18 - nie dotyczy) +0.220123-0 - Ramkę można teraz zmniejszać i zwiększać jednak bez ustawionych min. i max. +0.220122-3 - Dodałem plik makefile oraz desktop, oraz ikone png +0.220122-2 - Skrypt prawidłowo wyświetla zdjęcia, zarówno poziomo jak i pionowo. Ramka również jest prawidłowo rysowana - poza zdjęciami w formcie 4:3 ! +0.220122-1 - dodałem kompletną ramkę póki co nieruchomą, dodanie przycisków oraz combobox +0.220122-0 - Zaangażowanie klasy DrawingArea do wyświetlania i rysowania linii, póki co skrypt rysuje pojedynczą linie +0.220121-0 - Rozpoczęcie projektu, wyświetlenie okna z możliwością wczytania i wyświetlenia fotografii diff -r 000000000000 -r dcd610585610 photocrop/configure.gz Binary file photocrop/configure.gz has changed diff -r 000000000000 -r dcd610585610 photocrop/data/IMG_6854.JPG Binary file photocrop/data/IMG_6854.JPG has changed diff -r 000000000000 -r dcd610585610 photocrop/data/clip.png Binary file photocrop/data/clip.png has changed diff -r 000000000000 -r dcd610585610 photocrop/data/photocrop.desktop --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/data/photocrop.desktop Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,15 @@ +#!/usr/bin/env xdg-open + +[Desktop Entry] +Encoding=UTF-8 +Type=Application +Categories=Graphics; + +Name=Photo Crop + +Exec=python3 /usr/bin/photocrop.py +#Exec=gnome-terminal -e "bash -c 'python3 /usr/bin/photocrop.py;$SHELL'" +Terminal=false +Icon=/usr/share/photocrop/clip.png + +Name[pl_PL]=Przycinanie Zdjęć diff -r 000000000000 -r dcd610585610 photocrop/src/photocrop.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/photocrop/src/photocrop.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,1164 @@ +#!/usr/bin/env python3 +# Photo Crop - photo crop to size app to print in the most popular photo paper sizes +# author: Przemysław R. Pietraszczyk +# license: GPL v.2 +# date 21-01-2022 +# editor: Geany + +import sys +import cairo +import gi, os +import time +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GdkPixbuf, Gdk +from gi.repository.GdkPixbuf import Pixbuf + +UI_INFO = """ + + + + + + + + + + + + + + + + + + + + + +""" + +""" +# szkielet z podmenu + + + + + + + + + + + + + + + + +""" + +grid = Gtk.Grid() +rotate = False +file_img_selected = "" + +def get_resource_path(rel_path): + dir_of_py_file = os.path.dirname(__file__) + rel_path_to_resource = os.path.join(dir_of_py_file, rel_path) + abs_path_to_resource = os.path.abspath(rel_path_to_resource) + return abs_path_to_resource + +class FileChooserIMGLoad(Gtk.Window): + def __init__(self): + Gtk.Window.__init__(self, title="Selection of graphic files") + global file_img_selected + + dialog = Gtk.FileChooserDialog(title="Selection of graphic files", parent=self, action=Gtk.FileChooserAction.OPEN) + dialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, + ) + + self.add_filters(dialog) + + response = dialog.run() + if response == Gtk.ResponseType.OK: + print("Open clicked") + print("File selected: " + dialog.get_filename()) + file_img_selected=dialog.get_filename() + elif response == Gtk.ResponseType.CANCEL: + print("Cancel clicked") + + dialog.destroy() + + def add_filters(self, dialog): + + filter_jpeg = Gtk.FileFilter() + filter_jpeg.set_name("Files JPEG") + filter_jpeg.add_mime_type("image/jpeg") + dialog.add_filter(filter_jpeg) + + filter_png = Gtk.FileFilter() + filter_png.set_name("Files PNG") + filter_png.add_mime_type("image/png") + dialog.add_filter(filter_png) + """ + filter_png = Gtk.FileFilter() + filter_png.set_name("Files TIFF") + filter_png.add_mime_type("image/tiff") + dialog.add_filter(filter_png) + """ + +class FileChooserIMGSave(Gtk.Window): + def __init__(self): + Gtk.Window.__init__(self, title="Save the cropped photo") + global file_img_selected + + dialog = Gtk.FileChooserDialog(title="Save the cropped photo", parent=self, action=Gtk.FileChooserAction.SAVE) + dialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_SAVE, + Gtk.ResponseType.OK, + ) + + self.add_filters(dialog) + + response = dialog.run() + if response == Gtk.ResponseType.OK: + print("Open clicked") + print("File selected: " + dialog.get_filename()) + file_img_selected=dialog.get_filename() + elif response == Gtk.ResponseType.CANCEL: + print("Cancel clicked") + + dialog.destroy() + + def add_filters(self, dialog): + + filter_jpeg = Gtk.FileFilter() + filter_jpeg.set_name("Files JPEG") + filter_jpeg.add_mime_type("image/jpeg") + dialog.add_filter(filter_jpeg) + + filter_png = Gtk.FileFilter() + filter_png.set_name("Files PNG") + filter_png.add_mime_type("image/png") + dialog.add_filter(filter_png) + """ + filter_png = Gtk.FileFilter() + filter_png.set_name("Files TIFF") + filter_png.add_mime_type("image/tiff") + dialog.add_filter(filter_png) + """ + + +class DialogWarning(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="Attention!", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + self.set_default_size(150, 100) + label1 = Gtk.Label(label="The frame is outside the canvas!\n") + box = self.get_content_area() + box.add(label1) + self.show_all() + +class DialogCropWarning(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="Attention!", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + self.set_default_size(150, 100) + label1 = Gtk.Label(label="Crop the photo first!\n") + box = self.get_content_area() + box.add(label1) + self.show_all() + +class DialogHelp(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="Help", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + + self.set_default_size(150, 100) + label1 = Gtk.Label(label="An application for cropping photos to the most popular\nformats [13x18, 10x15, A4]\n\nScaling the size of the frame - mouse wheel with the CTRL key pressed\nMoving the frame - pressed LMB and moving the mouse\nin the desired direction.") + #label2 = Gtk.Label(lanel="") + box = self.get_content_area() + box.add(label1) + #box.add(label2) + self.show_all() + +class DialogAbout(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="About", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + + self.set_default_size(150, 100) + + label = Gtk.Label(label="\tThe application is based on:") + box = self.get_content_area() + box.add(label) + + button = Gtk.LinkButton("https://python-gtk-3-tutorial.readthedocs.io/", label="https://python-gtk-3-tutorial.readthedocs.io/") + box.add(button) + + label2 = Gtk.Label(label="\n\tVersion: 0.230112-1~alpha\n\n\tPrzemysław R. Pietraszczyk\n\n\t\t January 2022\n\n\n") + box.add(label2) + + + button = Gtk.LinkButton("https://prymula.ct8.pl", label="Site") + box.add(button) + + self.show_all() + +class DialogLicense(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="License", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + + self.set_default_size(150, 100) + + label = Gtk.Label(label="This program is distributed without any warranty. More information:\n") + + box = self.get_content_area() + box.add(label) + + button = Gtk.LinkButton("https://www.gnu.org/licenses/old-licenses/gpl-2.0.html", label="GNU General License version => 2") + box.add(button) + + self.show_all() + +class Brush(object): + + default_rgba_color = (0, 0, 0, 1) + + def __init__(self, width=None, rgba_color=None): + + if rgba_color is None: + rgba_color = self.default_rgba_color + + if width is None: + width = 3 + + self.__width = width + self.__rgba_color = rgba_color + self.__stroke = [] + self.__current_line = [] + + def _line_ended(self): + self.__stroke.append(self.__current_line.copy()) + self.__current_line = [] + + def _add_point(self, point): + self.__current_line.append(point) + + def _draw(self, cairo_context): + + cairo_context.set_source_rgba(*self.__rgba_color) + cairo_context.set_line_width(self.__width) + cairo_context.set_line_cap(cairo.LINE_CAP_ROUND) + + cairo_context.new_path() + for line in self.__stroke: + for x, y in line: + cairo_context.line_to(x, y) + cairo_context.new_sub_path() + + for x, y in self.__current_line: + cairo_context.line_to(x, y) + + cairo_context.stroke() + + +# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # +# ~ Getters & Setters ~ # +# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # + + def _get_width(self): + return self.__width + + def _set_width(self, width): + self.__width = width + + def _get_rgba_color(self): + return self.__rgba_color + + def _set_rgba_color(self, rgba_color): + self.__rgba_color = rgba_color + + def _get_stroke(self): + return self.__stroke + + def _get_current_line(self): + return self.__current_line + +class MyWindow(Gtk.Window): + + def __init__(self): + super().__init__() + + self.init_ui() + + def on_drawing_area_draw(self, drawable, cairo_context): + global rotate + + start = time.time() + self.brush = Brush() + + # DrawingArea size depends on Pixbuf size + #self.drawing_area.get_window().resize(self.displayed_pixbuf.get_width(), + # self.displayed_pixbuf .get_height()) + #self.drawing_area.set_size_request(self.displayed_pixbuf.get_width(), + # self.displayed_pixbuf.get_height()) + + + # (x, y) offsets + pixbuf_x = 0 #int(self.viewport.get_hadjustment().get_value()) + pixbuf_y = 0 # int(self.viewport.get_vadjustment().get_value()) + + # Width and height of the image's clip + width = cairo_context.get_target().get_width() + height = cairo_context.get_target().get_height() + + if width > 0 and height > 0: + + # Draw created area of the Sample's Pixbuf + Gdk.cairo_set_source_pixbuf(cairo_context, self.pixbuf_view, pixbuf_x, pixbuf_y) + + cairo_context.paint() + + if self.border_visible == True: + # Draw brush strokes + self.brush._add_point((self.border_x, self.border_y)) + self.brush._add_point((self.border_width, self.border_y)) + self.brush._add_point((self.border_width, self.border_y+self.border_height)) + self.brush._add_point((self.border_x, self.border_y+self.border_height)) + self.brush._add_point((self.border_x, self.border_y)) # nie działa ? + + self.brush._draw(cairo_context) + + end = time.time() + #print(f"Runtime of the program is {end - start}") + + + def set_ratio_border_size(self): + + self.border_x = 0 + self.border_y = 0 + + if self.format_size == "10x15": + if self.horizontal == True and self.vertical == False: + single = self.picture_view_height*0.5 + self.border_height = single + self.border_width = single*1.4 + #self.look = "horizontal" + #self.border_type = "horizontal" + elif self.horizontal == False and self.vertical == True: + single = self.picture_view_width*0.5 + self.border_width = single + self.border_height = single*1.4 + #self.look = "vertical" + #self.border_type = "vertical" + elif self.horizontal == False and self.vertical == False: + self.border_height = self.picture_view_height + self.border_width = self.picture_view_width + + elif self.format_size == "13x18": + if self.horizontal == True and self.vertical == False: + single = self.picture_view_height*0.5 + self.border_height = single + self.border_width = single*1.42 + elif self.horizontal == False and self.vertical == True: + single = self.picture_view_width*0.5 + self.border_width = single + self.border_height = single*1.42 + elif self.horizontal == False and self.vertical == False: + self.border_height = self.picture_view_height + self.border_width = self.picture_view_width + + elif self.format_size == "A4": + if self.horizontal == True and self.vertical == False: + single = self.picture_view_height*0.5 + self.border_height = single + self.border_width = single*1.4 + elif self.horizontal == False and self.vertical == True: + single = self.picture_view_width*0.5 + self.border_width = single + self.border_height = single*1.4 + elif self.horizontal == False and self.vertical == False: + self.border_height = self.picture_view_height + self.border_width = self.picture_view_width + + + + # ustaw proporcje dla obrazu w kontrolce + def set_ratio_picture_view(self): + self.picture_orig_width = float(self.pixbuf_orig.get_width()) + self.picture_orig_height = float(self.pixbuf_orig.get_height()) + #if self.format_size == "10x15": + #self.picture_orig_x = 0 + #self.picture_orig_y = 0 + + if self.picture_orig_width > self.picture_orig_height: + self.picture_view_width = 586.66 #600 + m = self.picture_orig_width / 586.66 #600 + self.picture_view_height = self.picture_orig_height/m + if self.picture_orig_height > self.picture_orig_width: + self.picture_view_height = 586.66 #600 + m = self.picture_orig_height / 586.66 #600 + self.picture_view_width = self.picture_orig_width/m + + # Zapamiętanie rozmiaru obrazka dla kontrolki + self.orig_ratio_width = self.picture_view_width + self.orig_ratio_height = self.picture_view_height + + + + # ustaw proporcje dla obrazu w kontrolce ppo rotacji + def set_ratio_picture_view_after_rotation(self): + + self.picture_tmp_width = self.border_width - self.border_x + self.picture_tmp_height = self.border_height - self.border_y + + if self.border_x != 0 : + bx = self.picture_orig_width / self.border_x + crop_x = self.picture_tmp_width / bx + else: + crop_x = 0 + + if self.border_y != 0: + by = self.picture_orig_height / self.border_y + crop_y = self.picture_tmp_height / by + else: + crop_y = 0 + + + self.picture_orig_x = crop_x + self.picture_orig_y = crop_y + """ + W zależności jaka była pierwotnie orientacja zdjęcia, po obrocie + jest ono odpowiedio przycinane + """ + print ("ORIG: "+self.look) + if self.look == "vertical": + if self.border_type == "horizontal": + bw = self.picture_orig_width / (self.border_width - self.border_x) + self.picture_tmp_width = self.picture_orig_width / bw - crop_x + # w tym wypadku 'y' bez odejmowania gdyż skróciło by to fotografie w pione + bh = self.picture_orig_height / self.border_height + self.picture_tmp_height = self.picture_orig_height / bh + + elif self.border_type == "vertical": + bw = self.picture_orig_width / (self.border_width - self.border_x) + self.picture_tmp_width = self.picture_orig_width / bw - crop_x + + bh = self.picture_orig_height / (self.border_height - self.border_y) + self.picture_tmp_height = self.picture_orig_height / bh + + elif self.look == "horizontal": + if self.border_type == "horizontal": + bw = self.picture_orig_width / (self.border_width - self.border_x) + self.picture_tmp_width = self.picture_orig_width / bw - crop_x + + bh = self.picture_orig_height / (self.border_height - self.border_y) + self.picture_tmp_height = self.picture_orig_height / bh + + elif self.border_type == "vertical": + bw = self.picture_orig_width / (self.border_width - self.border_x) + self.picture_tmp_width = self.picture_orig_width / bw - crop_x + + bh = self.picture_orig_height / (self.border_height - self.border_y) + self.picture_tmp_height = self.picture_orig_height / bh + + + if self.picture_tmp_width > self.picture_tmp_height: + self.picture_view_width = 586.66 + m = self.picture_tmp_width / 586.66 + self.picture_view_height = self.picture_tmp_height/m + if self.picture_tmp_height > self.picture_tmp_width: + self.picture_view_height = 586.66 + m = self.picture_tmp_height / 586.66 + self.picture_view_width = self.picture_tmp_width/m + + # Zapamiętanie rozmiaru obrazka dla kontrolki + self.orig_ratio_width = self.picture_view_width + self.orig_ratio_height = self.picture_view_height + + def on_menu_file_load_img_generic(self, widget): + global grid, file_img_selected + + filename=FileChooserIMGLoad() + + if len(file_img_selected) != 0: + + self.pixbuf_orig = GdkPixbuf.Pixbuf.new_from_file(filename=file_img_selected) + + self.set_ratio_picture_view() + self.pixbuf_view = self.pixbuf_orig.scale_simple(self.picture_view_width, self.picture_view_height, GdkPixbuf.InterpType.HYPER) + + if self.picture_view_width > self.picture_view_height: + self.horizontal = True + self.vertical = False + self.resize(582.66,413.34) + self.look = "horizontal" + self.border_type = "horizontal" + elif self.picture_view_height > self.picture_view_width: + self.vertical = True + self.horizontal = False + self.resize(413.34,582.66) + self.look = "vertical" + self.border_type = "vertical" + else: + self.horizontal = False + self.vertical = False + + + self.set_ratio_border_size() + + + + self.border_visible = True + + # nie rysujemy na orginale ale na kopii z okna + self.drawing_area.set_size_request(self.pixbuf_view.get_width(), self.pixbuf_view.get_height()) + self.drawing_area.set_events(Gdk.EventMask.ALL_EVENTS_MASK) + + self.show_all() + + def add_edit_menu_actions(self, action_group): + action_info_menu = Gtk.Action(name="EditMenu", label="Edit") + action_group.add_action(action_info_menu) + + action_new = Gtk.Action( + name="Rotate", + label="Frame rotation", + tooltip="Rotate border", + ) + action_new.connect("activate", self.on_menu_rotate) + action_group.add_action_with_accel(action_new, None) + + + def add_info_menu_actions(self, action_group): + action_info_menu = Gtk.Action(name="InfoMenu", label="Info") + action_group.add_action(action_info_menu) + + action_new = Gtk.Action( + name="Help", + label="Help", + tooltip="Help", + ) + action_new.connect("activate", self.on_menu_help) + action_group.add_action_with_accel(action_new, None) + + action_new = Gtk.Action( + name="About", + label="About", + tooltip="About", + ) + action_new.connect("activate", self.on_menu_about) + action_group.add_action_with_accel(action_new, None) + + action_new = Gtk.Action( + name="License", + label="License", + tooltip="License", + ) + action_new.connect("activate", self.on_menu_license) + action_group.add_action_with_accel(action_new, None) + + + + def add_file_menu_actions(self, action_group): + action_filemenu = Gtk.Action(name="FileMenu", label="File") + action_group.add_action(action_filemenu) + + """ + # sposób dodawania podmenu + action_fileloadmenu = Gtk.Action(name="FileLoad", stock_id=Gtk.STOCK_OPEN) + action_group.add_action(action_fileloadmenu) + + action_new = Gtk.Action( + name="LoadImg", + label="Wczytaj Obrazek", + tooltip="Wczytuje obrazek", + ) + action_new.connect("activate", self.on_menu_file_load_img_generic) + action_group.add_action_with_accel(action_new, None) + """ + + action_fileload = Gtk.Action(name="FileLoad", stock_id=Gtk.STOCK_OPEN) + action_fileload.connect("activate", self.on_menu_file_load_img_generic) + action_group.add_action(action_fileload) + + action_filesave = Gtk.Action(name="FileSave", stock_id=Gtk.STOCK_SAVE) + action_filesave.connect("activate", self.on_menu_file_save_img) + action_group.add_action(action_filesave) + + + action_print = Gtk.Action(name="FilePrint", stock_id=Gtk.STOCK_PRINT) + action_print.connect("activate", self.print_image) + action_group.add_action(action_print) + + + action_filequit = Gtk.Action(name="FileQuit", stock_id=Gtk.STOCK_QUIT) + action_filequit.connect("activate", self.on_menu_file_quit) + action_group.add_action(action_filequit) + + + + # tworzy menu bar + def create_ui_manager(self): + uimanager = Gtk.UIManager() + + # Throws exception if something went wrong + uimanager.add_ui_from_string(UI_INFO) + + # Add the accelerator group to the toplevel window + accelgroup = uimanager.get_accel_group() + self.add_accel_group(accelgroup) + return uimanager + + def on_menu_about(self, widget): + dialog = DialogAbout(self) + response = dialog.run() + + dialog.destroy() + + def on_menu_license(self, widget): + dialog = DialogLicense(self) + response = dialog.run() + + dialog.destroy() + + + def on_menu_help(self, widget): + dialog = DialogHelp(self) + response = dialog.run() + + dialog.destroy() + + def on_menu_rotate(self, widget): + global rotate + + + if self.border_type == "horizontal": + self.border_type = "vertical" + self.horizontal = False + self.vertical = True + + single = self.picture_view_width*0.3 + self.border_width = single + self.border_height = single*1.4 + self.border_x = 0 + self.border_y = 0 + + rotate = True + + elif self.border_type == "vertical": + self.border_type = "horizontal" + self.horizontal = True + self.vertical = False + + single = self.picture_view_height*0.3 + self.border_height = single + self.border_width = single*1.4 + self.border_x = 0 + self.border_y = 0 + + rotate = True + else: + pass + + print ("ASPECT: "+self.border_type) + print ("BORDER-WIDTH :"+str(self.border_width)) + print ("BORDER-HEIGHT:"+str(self.border_height)) + + self.drawing_area.queue_draw() + + def on_menu_file_save_img(self, widget): + global file_img_selected + + if self.pixbuf_tmp != None: + filename=FileChooserIMGSave() + if len(file_img_selected) != 0: + self.pixbuf_view.savev(file_img_selected, "jpeg", ["quality"], ["100"]) + else: + dialog = DialogCropWarning(self) + response = dialog.run() + + dialog.destroy() + + def on_menu_file_quit(self, widget): + Gtk.main_quit() + + def print_page(self, operation=None, context=None, page_nr=None): + + ctx = context.get_cairo_context() + + # make cairo ImageSurface from the png file + surface = cairo.ImageSurface.create_from_png('/tmp/photocrop.png') + #ctx.rectangle(50,50,100,100) + ctx.set_source_surface(surface) + ctx.paint () + os.remove("/tmp/photocrop.png"); + + + def print_image(self, widget): + + if self.pixbuf_tmp == None: + self.pixbuf_tmp = self.pixbuf_orig + + + # źle obraca + #if self.border_type == "horizontal": + # self.pixbuf_tmp.rotate_simple(GdkPixbuf.PixbufRotation.COUNTERCLOCKWISE) + # #pixbuf2.rotate_simple(GdkPixbuf.PixbufRotation.CLOCKWISE) + # self.border_type = "vertical" + + FACTOR_MM_TO_PIXEL = 2.834645669 + + + if self.format_size == "10x15": + #if self.horizontal == True and self.vertical == False: + if self.border_type == "horizontal": + page_width = 148 + page_height = 104.99 + + img_height =104.99 * FACTOR_MM_TO_PIXEL + img_width = 148 * FACTOR_MM_TO_PIXEL + #elif self.horizontal == False and self.vertical == True: + elif self.border_type == "vertical": + + page_width = 104.99 + page_height = 148 + + img_width =104.99 * FACTOR_MM_TO_PIXEL + img_height = 148 * FACTOR_MM_TO_PIXEL + + size = "10x15" + elif self.format_size == "13x18": + #if self.horizontal == True and self.vertical == False: + if self.border_type == "horizontal": + + page_width = 178 + page_height = 127 + + img_height = 127 * FACTOR_MM_TO_PIXEL + img_width = 178 * FACTOR_MM_TO_PIXEL + #elif self.horizontal == False and self.vertical == True: + elif self.border_type == "vertical": + + page_width = 127 + page_height = 178 + + img_width = 127 * FACTOR_MM_TO_PIXEL + img_height = 178 * FACTOR_MM_TO_PIXEL + size = "5x7" + elif self.format_size == "A4": + #if self.horizontal == True and self.vertical == False: + if self.border_type == "horizontal": + page_width = 297 + page_height = 207 + + img_height = 207 * FACTOR_MM_TO_PIXEL + img_width = 297 * FACTOR_MM_TO_PIXEL + #elif self.horizontal == False and self.vertical == True: + elif self.border_type == "vertical": + + page_width = 207 + page_height = 297 + + img_width = 207 * FACTOR_MM_TO_PIXEL + img_height = 297 * FACTOR_MM_TO_PIXEL + + size = "A4" + + dpi = 600 + + # z orginalnymi wielkościami nie chce drukowac + pixbuf2 = self.pixbuf_tmp.scale_simple(img_width, img_height, GdkPixbuf.InterpType.HYPER) + + pixbuf2.savev("/tmp/photocrop.png","png", ["quailty"], ["100"]) + + #ps = Gtk.PaperSize.new_custom(size, size, self.pixbuf_tmp.get_width(), self.pixbuf_tmp.get_height(), Gtk.Unit.POINTS) + ps = Gtk.PaperSize.new_custom(size, size, page_width, page_height, Gtk.Unit.MM) + + print_settings = Gtk.PrintSettings() + print_settings.set_resolution(dpi) + + page_setup = Gtk.PageSetup() + page_setup.set_paper_size(ps) + page_setup.set_bottom_margin(0.0, Gtk.Unit.MM) + page_setup.set_left_margin(0.0, Gtk.Unit.MM) + page_setup.set_right_margin(0.0, Gtk.Unit.MM) + page_setup.set_top_margin(0.0, Gtk.Unit.MM) + + #if self.border_type == "horizontal": + # page_setup.set_orientation(Gtk.PageOrientation.LANDSCAPE) + #elif self.border_type == "vertical": + # page_setup.set_orientation(Gtk.PageOrientation.PORTRAIT) + + + print_operation = Gtk.PrintOperation() + print_operation.set_unit(Gtk.Unit.POINTS) + print_operation.set_n_pages(1) + print_operation.set_default_page_setup(page_setup) + print_operation.set_print_settings(print_settings) + print_operation.connect("draw_page", self.print_page) + #print_operation.set_export_filename("example.pdf") + + result = print_operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, None) # window zamisat None + + #result = print_operation.run(Gtk.PrintOperationAction.PREVIEW, None) + print(result) + + # przycinamy ! + def photo_crop(self, button): + + if self.border_x < 0: + dialog = DialogWarning(self) + response = dialog.run() + dialog.destroy() + return + if self.border_y < 0: + dialog = DialogWarning(self) + response = dialog.run() + dialog.destroy() + return + if self.border_width > self.picture_view_width: + dialog = DialogWarning(self) + response = dialog.run() + dialog.destroy() + return + # FIXME - w sumie to jest zastanawiające !? + if self.border_height + self.border_y > self.picture_view_height: + dialog = DialogWarning(self) + response = dialog.run() + dialog.destroy() + return + + + if self.border_x != 0 : + bx = self.picture_view_width / self.border_x + crop_x = self.picture_orig_width / bx + else: + crop_x = 0 + + if self.border_y != 0: + by = self.picture_view_height / self.border_y + crop_y = self.picture_orig_height / by + else: + crop_y = 0 + + bw = self.picture_view_width / self.border_width + crop_width = self.picture_orig_width / bw - crop_x + + bh = self.picture_view_height / self.border_height + crop_height = self.picture_orig_height / bh + + + # False - kanał Alpha + self.pixbuf_tmp = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, crop_width, crop_height) + + # zera na koncu to dest_x i dest_y + self.pixbuf_orig.copy_area(crop_x, crop_y, crop_width, crop_height, self.pixbuf_tmp, 0, 0) + + if rotate == False: + + self.picture_tmp_width = float(self.pixbuf_tmp.get_width()) + self.picture_tmp_height = float(self.pixbuf_tmp.get_height()) + + # tym razem przeliczamy z uwzględnieniem bufora tymczasowego + if self.picture_tmp_width > self.picture_tmp_height: + self.picture_view_width = 586.66 + m = self.picture_tmp_width / 586.66 + self.picture_view_height = self.picture_tmp_height/m + if self.picture_tmp_height > self.picture_tmp_width: + self.picture_view_height = 586.66 + m = self.picture_tmp_height / 586.66 + self.picture_view_width = self.picture_tmp_width/m + + + + else: + self.set_ratio_picture_view_after_rotation() + + self.drawing_area.set_size_request(self.picture_view_width, self.picture_view_height) + self.drawing_area.set_events(Gdk.EventMask.ALL_EVENTS_MASK) + + self.pixbuf_view = self.pixbuf_tmp.scale_simple(self.picture_view_width, self.picture_view_height, GdkPixbuf.InterpType.HYPER) + + self.picture_view_x = 0 + self.picture_view_y = 0 + + self.border_visible = False + + self.drawing_area.queue_draw() + + # przywracamy + def photo_restore(self, button): + + self.border_visible = True + + self.set_ratio_picture_view() + self.pixbuf_view = self.pixbuf_orig.scale_simple(self.orig_ratio_width, self.orig_ratio_height, GdkPixbuf.InterpType.BILINEAR) + + self.drawing_area.set_size_request(self.orig_ratio_width, self.orig_ratio_height) + self.drawing_area.set_events(Gdk.EventMask.ALL_EVENTS_MASK) + self.pixbuf_tmp = None # uniemożliwiamy zapis + self.show_all() + + self.drawing_area.queue_draw() + + + def on_format_combo_changed(self, combo): + tree_iter = combo.get_active_iter() + if tree_iter is not None: + model = combo.get_model() + self.format_size = model[tree_iter][0] + print("Selected: format=%s" % self.format_size) + + self.set_ratio_border_size() + + self.drawing_area.queue_draw() + + def on_scroll(self, widget, event): + """ handles on scroll event""" + # Handles zoom in / zoom out on Ctrl+mouse wheel + accel_mask = Gtk.accelerator_get_default_mod_mask() + if event.state & accel_mask == Gdk.ModifierType.CONTROL_MASK: + direction = event.get_scroll_deltas()[2] + + if direction > 0: + scrolling = "zoom_out" + else: + scrolling = "zoom_in" + + self.border_height += self.border_properties[self.format_size][self.border_type][scrolling]["height"] + self.border_width += self.border_properties[self.format_size][self.border_type][scrolling]["width"] + """ + # tu jest jakiś błąd + if self.border_width > self.picture_view_width: + self.border_width -= 1 + if self.border_height+self.border_y > self.picture_view_height: + self.border_height -= 1 + """ + self.drawing_area.queue_draw() + + def unclick_in_drawing_area (self, box, event): + self.button_press = False + print ("Przycisk myszki puszczony") + + + def onclick_in_drawing_area (self, box, event): + if event.button == 1: + self.button_press = True + print ("Lewy przyciski myszki naciśnięty") + + def on_mouse_move_in_drawing_area(self, box, event): + + + if self.button_press == True: + + if self.border_type == "vertical": + + #print ("VERTICAL %%") + + if self.last_x < event.x: + self.border_x += 1 + self.border_width += 1 + if event.x < self.last_x: + self.border_x -= 1 + self.border_width -= 1 + if self.last_y < event.y: + self.border_y += 1 + self.border_height += 0.0 + if event.y < self.last_y: + self.border_y -= 1 + self.border_height -= 0.0 + + elif self.border_type == "horizontal": + #print ("Horizontal %%") + if self.last_x < event.x: + self.border_x += 1 + self.border_width += 1 + if event.x < self.last_x: + self.border_x -= 1. + self.border_width -= 1 + if self.last_y < event.y: + self.border_y += 1 + self.border_height += 0.0 + if event.y < self.last_y: + self.border_y -= 1 + self.border_height -= 0.0 + + self.last_y = event.y + self.last_x = event.x + """ + # jeśli będzie się napierać na skraj krawędzi wówczas powiększa ramkę + if self.border_x < 0: + self.border_x += 1 + self.border_width += 1 + if self.border_y < 0: + self.border_y += 1 + self.border_height +=1 + # tu jest jakiś błąd + + # powoduje błędne zachowanie ramki + if self.border_type == "vertical": + if self.border_width > self.picture_view_width: + self.border_width -= 1 + self.border_x -= 1 + if self.border_height+self.border_y > self.picture_view_height: + self.border_height -= 1 + self.border_y -= 1 + elif self.border_type == "horizontal": + if self.border_height+self.border_y> self.picture_view_width: + self.border_width -= 1 + self.border_x -= 1 + if self.border_width > self.picture_view_height: + self.border_height -= 1 + self.border_y -= 1 + """ + + self.drawing_area.queue_draw() + + def init_ui(self): + # JPG akceptuje jedynie z GIMPa + self.border_properties = { "10x15" : { "horizontal" : { "zoom_out" : {"width" : -1.48, "height" : -0.92}, "zoom_in" : {"width" : 1.48, "height" : 0.92}}, + "vertical" : { "zoom_out": { "width" : -0.92, "height" : -1.48}, "zoom_in": { "width" : 0.92, "height" : 1.48}}, + "square" : { "zoom_out" : { "width" : -1, "height" : -1}, "zoom_out" : { "width" : 1, "height" : 1}}}, + + "13x18" : { "horizontal" : { "zoom_out" : {"width" : -1.82, "height" : -1.30}, "zoom_in" : {"width" : 1.82, "height" : 1.30}}, + "vertical" : { "zoom_out": { "width" : -1.30, "height" : -1.78}, "zoom_in": { "width" : 1.30, "height" : 1.78}}, + "square" : { "zoom_out" : { "width" : -1, "height" : -1}, "zoom_out" : { "width" : 1, "height" : 1}}}, + + "A4" : { "horizontal" : { "zoom_out" : {"width" : -2.97, "height" : -1.84}, "zoom_in" : {"width" : 2.97, "height" : 1.84}}, + "vertical" : { "zoom_out": { "width" : -1.84, "height" : -2.97}, "zoom_in": { "width" : 1.84, "height" : 2.97}}, + "square" : { "zoom_out" : { "width" : -1, "height" : -1}, "zoom_out" : { "width" : 1, "height" : 1}}}} + + + #self.props.border_width = 20 + self.add(grid) + self.pixbuf_tmp = None + grid.set_row_spacing(10) + grid.set_column_spacing(10) + grid.set_column_homogeneous(True) # rozszerza kontrolki na resztę okna + + action_group = Gtk.ActionGroup(name="my_actions") + + self.add_file_menu_actions(action_group) + self.add_edit_menu_actions(action_group) + self.add_info_menu_actions(action_group) + + uimanager = self.create_ui_manager() + uimanager.insert_action_group(action_group) + + menubar = uimanager.get_widget("/MenuBar") + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + box.pack_start(menubar, False, False, 0) + + grid.attach(box, 0, 0, 3, 1) + + + file_img_selected=get_resource_path("/usr/share/photocrop/IMG_6854.JPG") #img/blank.png + self.pixbuf_orig = GdkPixbuf.Pixbuf.new_from_file(filename=file_img_selected) + + self.format_size = "10x15" + self.set_ratio_picture_view() + self.pixbuf_view = self.pixbuf_orig.scale_simple(self.picture_view_width, self.picture_view_height, GdkPixbuf.InterpType.HYPER) + + self.border_visible = True + + if self.picture_view_width > self.picture_view_height: + self.horizontal = True + self.vertical = False + self.look = "horizontal" + self.border_type = "horizontal" + elif self.picture_view_height > self.picture_view_width: + self.vertical = True + self.horizontal = False + self.look = "vertical" + self.border_type = "vertical" + else: + self.horizontal = False + self.vertical = False + self.look = "square" + + self.drawing_area = Gtk.DrawingArea() + + self.drawing_area.set_size_request(self.pixbuf_view.get_width(), self.pixbuf_view.get_height()) + self.drawing_area.set_events(Gdk.EventMask.ALL_EVENTS_MASK) + + self.drawing_area.connect("draw", self.on_drawing_area_draw) + + frame = Gtk.Frame() + event_box = Gtk.EventBox () + self.last_x = 1 + self.last_y = 1 + self.border_x = 0 + self.border_y = 0 + self.button_press = False + self.pixbuf_tmp = None + event_box.connect ('button-press-event', self.onclick_in_drawing_area) + event_box.connect ('button-release-event', self.unclick_in_drawing_area) + event_box.connect("motion-notify-event", self.on_mouse_move_in_drawing_area) + event_box.add_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.BUTTON_PRESS_MASK) + + event_box.add(self.drawing_area) + frame.add(event_box) + grid.attach(frame,0,1,3,1) + + button1 = Gtk.Button.new_with_label("Crop") + button1.connect("clicked", self.photo_crop) + grid.attach(button1,0,4,1,1) + + button2 = Gtk.Button.new_with_label("Restore") + button2.connect("clicked", self.photo_restore) + grid.attach(button2,1,4,1,1) + + format_store = Gtk.ListStore(str) + format_photo = [ + "10x15", + "13x18", + "A4", + ] + for fp in format_photo: + format_store.append([fp]) + + format_combo = Gtk.ComboBox.new_with_model(format_store) + format_combo.connect("changed", self.on_format_combo_changed) + renderer_text = Gtk.CellRendererText() + format_combo.pack_start(renderer_text, True) + format_combo.add_attribute(renderer_text, "text", 0) + format_combo.set_active(0) + grid.attach(format_combo,2,4,1,1) + + self.drawing_area.connect('scroll-event', self.on_scroll) + + self.set_border_width(10) + self.set_title("Photo Crop (alpha)") + #self.set_default_size(700, 600) + #self.resize(700, 600) + self.set_resizable(False) + self.connect("destroy", Gtk.main_quit) + + + +win = MyWindow() +win.show_all() +Gtk.main() diff -r 000000000000 -r dcd610585610 photocrop/xcf/clip.xcf Binary file photocrop/xcf/clip.xcf has changed diff -r 000000000000 -r dcd610585610 postagelabels/DEBIAN/HOWTO.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/DEBIAN/HOWTO.txt Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,2 @@ +foramt zapisu nazwy pliku: + photocrop_0.221225-0~alpha.tar.gz diff -r 000000000000 -r dcd610585610 postagelabels/DEBIAN/debian.changelog --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/DEBIAN/debian.changelog Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,5 @@ +photocrop (0.220120-2~beta) unstable; urgency=low + + * Last release + + -- Przemysław R. Pietraszczyk Sat, 25 Dec 2022 08:31:41 +0200 diff -r 000000000000 -r dcd610585610 postagelabels/DEBIAN/debian.control --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/DEBIAN/debian.control Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,13 @@ +Source: postagelabels +Section: python +Priority: extra +Maintainer: Przemysław R. Pietraszczyk +Build-Depends: debhelper-compat (= 13), python3-all, python-setuptools, dh-python, python3-gi, python3-gi-cairo, gir1.2-gtk-3.0, fakeroot +Standards-Version: 0.220120-2~beta +Homepage: http://pietraszczyk.vxm.pl + + +Package: postagelabels +Architecture: all +Depends: ${misc:Depends}, python3-all, python3-gi, python3-gi-cairo, gir1.2-gtk-3.0 +Description: An application for cropping photos to popular paper sizes. diff -r 000000000000 -r dcd610585610 postagelabels/DEBIAN/debian.rules --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/DEBIAN/debian.rules Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,25 @@ +#!/usr/bin/make -f + +export DH_VERBOSE = 1 + +clean: + @ + +build: + @ + +binary: + mkdir -pm 0755 debian/postagelabels + mkdir -pm 0755 debian/postagelabels/usr + mkdir -pm 0755 debian/postagelabels/usr/bin + mkdir -pm 0755 debian/postagelabels/usr/share + mkdir -pm 0755 debian/postagelabels/usr/share/postagelabels + mkdir -pm 0755 debian/postagelabels/usr/share/applications + cp postagelabels.py debian/postagelabels/usr/bin + cp png/postagelabels.png debian/postagelabels/usr/share/postagelabels + cp png/blank_small.png debian/postagelabels/usr/share/postagelabels + cp png/szablon.png debian/postagelabels/usr/share/postagelabels + cp postagelabels.desktop debian/postagelabels/usr/share/applications + dh_gencontrol + dh_builddeb + diff -r 000000000000 -r dcd610585610 postagelabels/DEBIAN/format.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/DEBIAN/format.sh Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,1 @@ +exec /usr/lib/build/debtransform ./ ./photocrop.dsc ./exec diff -r 000000000000 -r dcd610585610 postagelabels/DEBIAN/photocrop.dsc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/DEBIAN/photocrop.dsc Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,41 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +Format: 1.0 +Source: postagelabels +Binary: postagelabels +Architecture: all +Version: 0.220120-2~beta +Maintainer: Przemysław R. Pietraszczyk +Homepage: http://pietraszczyk.vxm.pl +DEBTRANSFORM-TAR: debian.tar.gz +DEBTRANSFORM-TAR: postagelabels_0.220120-2~beta.tar.gz + +Standards-Version: 0.220120-2~beta +Build-Depends: debhelper-compat (= 13), python3-all, python-setuptools, dh-python, python3-gi, python3-gi-cairo, gir1.2-gtk-3.0, fakeroot +Package-List: + photocrop deb x11 optional arch=all +Files: + b31649bc23b2b7bca9ab70ea3410711a 128820 postagelabels_0.220120-2~beta.tar.gz + 29e7e7e60bc81891e3da3871b9eeb549 764 postagelabels-0.220120-2~beta.debian.tar.xz + +-----BEGIN PGP SIGNATURE----- + +mQENBGEf/c4BCAC2d4ymW0pRZV36qLtlt/WGb83kos5UX5UbwvkQrbUjAbOPOY8w +DT3M1neYOAPZx38924aPTFKbZPcz+rK/7Wcv1kzgfux5zXQJTLeqpkhUYAgXUg2p +oK9ZXsai82fOicTrClOyJLLYQ8C1vj8yUh7e3ERljEyp5Nxg/lY92rwcZ4WYR193 +nGDInlDl5JlWUcLlk/RFnC5bB+T6ZZr5FBX/eDnKAPEl+N3MWpDs2JLDy7EUPhcG +U/60X0wuXHPTwMoNbB3ep/bWRxwEej0sFad5GXeCh7hKCroi/kLmLTDIxSD8lsRr +Y4H/8H2lBBsQq7bRL88N6ZDGCocPo11V83kFABEBAAG0NmhvbWU6cHJ6ZW0gT0JT +IFByb2plY3QgPGhvbWU6cHJ6ZW1AYnVpbGQub3BlbnN1c2Uub3JnPokBPgQTAQgA +KAUCYR/9zgIbAwUJBB6wAAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQcXEc +NPcNefFEAAf+Kg4cs/C99kEIMv/PO85VW+p8rcTwCViOFUX5WrBhwsRyPAKrJPPG +KD6PdnvoplcNQwhQ8nRsRiiaiUtPcj8Bvi6EdPgSgXXObKKJtCyaAa1rURdlb9iW +CfvXA6oW+PGlii9BMnXOAtkeFIzZooO9oXd+wez66P+4VWFiFzxanLfLn/hwbWxQ +68rlO3QNGbrMeODBgwh9s83XGmAg47mn2fldTAmTbJDf9tMi1LhX2MmLyN9wjqsd +4IZ9JMri2JTnIhYfxWdKT9gsu7q0g8LqonaXMvlOEyHFRF1YQ0i7G6fFbvDcKCZN +W1Eja55FdN7smv5Eck6UTIIYyWPuAv45uohGBBMRAgAGBQJhH/3PAAoJEDswEbdr +nWUj4M0AniJ389dyvUjjgS91AcOb08E4moV9AJ9WDL3/7GYYUL/Azw43QucWPnXr +uw== +=+lfH +-----END PGP SIGNATURE----- diff -r 000000000000 -r dcd610585610 postagelabels/RPM/postagelabels.spec --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/RPM/postagelabels.spec Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,69 @@ +# +# spec file for package postagelabels +# +# Copyright (c) 2020 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + +%define unmangled_version 0.220120-2~beta +Name: postagelabels +Version: 0.220120 +Release: 2~beta +Summary: Grid and print postal labels. +License: GPL +URL: http://prp.xlx.pl +Source0: %{name}-%{unmangled_version}.tar.gz +%if 0%{?suse_version}==1530 +BuildRequires: python3, python3-gobject, python3-gobject-Gdk, typelib-1_0-Gtk-3_0, libgtk-3-0 +Requires: python3, python3-gobject, python3-gobject-Gdk, typelib-1_0-Gtk-3_0, libgtk-3-0 +%else +BuildRequires: python3, python3-gobject, gtk3 +Requires: python3, python3-gobject, gtk3 +%endif +BuildArch: noarch + +%description + + +%prep +%setup -n %{name}-%{unmangled_version} -n %{name}-%{unmangled_version} + +%build + +%install + +mkdir -p %{buildroot}/usr +mkdir -p %{buildroot}/usr/share +mkdir -p %{buildroot}/usr/share/postagelabels +mkdir -p %{buildroot}/usr/share/applications +mkdir -p %{buildroot}/usr/bin + +install -m 0755 postagelabels.py %{buildroot}/usr/bin +install -m 0644 png/postagelabels.png %{buildroot}/usr/share/postagelabels/postagelabels.png +install -m 0644 png/blank_small.png %{buildroot}/usr/share/postagelabels/blank_small.png +install -m 0644 png/szablon.png %{buildroot}/usr/share/postagelabels/szablon.png +install -m 0644 postagelabels.desktop %{buildroot}/usr/share/applications + + + +%files +%defattr(0755,root,root) +/usr/bin/postagelabels.py +/usr/share/postagelabels/ +%defattr(0644,root,root) +/usr/share/postagelabels/postagelabels.png +/usr/share/postagelabels/blank_small.png +/usr/share/postagelabels/szablon.png +/usr/share/applications/postagelabels.desktop + +%changelog diff -r 000000000000 -r dcd610585610 postagelabels/changelog.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/changelog.txt Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,63 @@ +0.220120-0 - kolejna zmiana numeracji, zabezpieczyłem odczyt anulowanej etykiety oraz wyjustowałem prawidłowo etykiety na siatce + +0.30-0 - dopisałem nadpisywanie siatki nowym szablonem + +0.29-0 - dodanie odstępów w kreatorze etykiet, dodanie pliku makefile + +0.28-0 - kosmetyka, czyszcenie kodu + +0.27-0 - utworzoną etykietę skrypt zapisuje i dodaje do listy dostępnych etykiet + +0.26-0 - zmiana numeracji, dodanie okna dialogowego z zapisem (póki co jeszcze nie zapisuje), dodanie przycisku "wyczyć" w kompozytorze + +0.0-25 - dodanie kompozytora etykiet, póki co kompozycja w oknie + +0.0-24 - kosmetyka + +0.0-23 – powrót do skalowania obrazu przed drukowaniem + +0.0-22 – dodałem ‘self.show_all()’ w funkcji ‘onclick_in_pixbuf’ zobaczymy czy pomoże na odświeżanie siatki po dodaniu do niej etykiety. + +0.0-21 – przymiarki do pakietowania, po instalacji pakietu okazało się że nie odświeża siatki i nie widać dodawanych etykiet. + +0.0-20 - sprawdziłem jak skrypt zachowuje się w SUSE - efekt taki że do funkcji savev dodałem jeszcze dwa argumenty nt. jakości obrazu + +0.0-19 – ładnie ustawia etykiety na stronie do wydruku, jednak nie drukuje bezpośrednio. Należy najpierw zapisać plik PDF do wydruku. Póki co brakuje kompozytora etykiet. + +0.0-18 – poprawiłem nieco ułożenie etykiet na docelowym płótnie, jednak wynik jest inny od oczekiwanego. Na skrajnych górnych pozycjach myszki na najniższej siatce kiepsko odczytuje jej położenie. + +0.0-17 – drukuje etykiety ułożone w siatkę, jednak trzeba uwzględnić marginesy z kartki ‘Galerii Papieru’ + +0-0.16 – przygotowuje prawidłowy plik do druku, jednak drukuje w znacznym powiększeniu + +0.0-15 – drukuje ale pojedynczą etykietę, oraz znacznie powiększoną + +0.0-14 – przygotowano możliwość druku, ale nie drukuje – efekt pustej strony + +0.0-13 – dodano listę z danymi do druku + +0.0-12 – wyświetla okno dialogowe z informacją w przypadku braku zaznaczenia z listy dostępnych etykiet. Modyfikacje przy liście dostępnych etykiet – błąd krytyczny naprawiony, odświeżanie kontrolki również. + +0.0-11 – usiłuje wydrukować przykładowy obrazek – bez skutku + +0.0-10 – poprawiłem punkty odniesienia. Prawidłowo umieszcza i kasuje etykiety na siatce + +0.0-9 – wyświetla w siatce etykiety, po wcześniejszym zaznaczeniu na liście + +0.0-8 – pola dla etykiet w siatce po prawej są pozycjonowane „równo”, są kłopoty z właściwym wyświetleniem etykiety po naciśnięciu lewego przycisku myszy + +0.0-7 – wyświetla poszczególne etykiety w siatce po prawej stronie, ale nie równo. + +0.0-6 – podzieliłem główny obrazek po prawej stronie na części + +0.0-5 – rysuje zaznaczenie na self.image1, jednak kasując całkowicie poprzedni obraz. Robiąc przy okazji roszadę w ui. + +0.0-4 – odczytuje pozycje myszy na self.image1 po naciśnięciu klawisza myszy + +0.0-3 – nieśmiała przymiarka do drag and drop -nieudana + +0.0-2 – zaznaczenie nazwy etykiety z listy, zmienia obraz etykiety + +0,0-1 – dodałem wczytywanie etykiety, wyświetlenia jej w polu, oraz dodanie nazwy etykiety do listy, znajdującej się poniżej wizerunku etykiety. + +0.0-0 – inicjacja. diff -r 000000000000 -r dcd610585610 postagelabels/makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/makefile Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,22 @@ + +prefix=/usr + +install: + mkdir -pm 0755 $(prefix)/share/postagelabels + install -m 0755 postagelabels.py $(prefix)/bin + install -m 0644 png/postagelabels.png $(prefix)/share/postagelabels + install -m 0644 png/blank_small.png $(prefix)/share/postagelabels + install -m 0644 png/szablon.png $(prefix)/share/postagelabels + install -m 0644 postagelabels.desktop /usr/share/applications + +.PHONY: install + +uninstall: + rm $(prefix)/share/postagelabels/postagelabels.png + rm $(prefix)/share/postagelabels/blank_small.png + rm $(prefix)/share/postagelabels/szablon.png + rmdir $(prefix)/share/postagelabels/ + rm $(prefix)/bin/postagelabels.py + rm /usr/share/applications/postagelabels.desktop + +.PHONY: uninstall diff -r 000000000000 -r dcd610585610 postagelabels/png/blank_small.png Binary file postagelabels/png/blank_small.png has changed diff -r 000000000000 -r dcd610585610 postagelabels/png/postagelabels.png Binary file postagelabels/png/postagelabels.png has changed diff -r 000000000000 -r dcd610585610 postagelabels/png/szablon.png Binary file postagelabels/png/szablon.png has changed diff -r 000000000000 -r dcd610585610 postagelabels/postagelabels.desktop --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/postagelabels.desktop Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,15 @@ +#!/usr/bin/env xdg-open + +[Desktop Entry] +Encoding=UTF-8 +Type=Application +Categories=Office; + +Name=Postage Labels + +Exec=python3 /usr/bin/postagelabels.py +#Exec=gnome-terminal -e "bash -c 'python3 /usr/bin/postagelabels.py;$SHELL'" +Terminal=false +Icon=/usr/share/postagelabels/postagelabels.png + +Name[pl_PL]=Naklejki Pocztowe diff -r 000000000000 -r dcd610585610 postagelabels/postagelabels.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/postagelabels/postagelabels.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,1012 @@ +#!/usr/bin/env python3 +# Postage Labels - aplikacja do aranżacji wydruków etykiet pocztowych +# autor: Przemysław R. Pietraszczyk +# licencja: GPL +# data 20-IX-2021 +# edytor: Geany + +import sys +import cairo +import gi, os +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GdkPixbuf, Gdk +from gi.repository.GdkPixbuf import Pixbuf + + +UI_INFO = """ + + + + + + + + + + + + + + + + + + + +""" + +grid = Gtk.Grid() +file_img_selected="" +file_label_save="" +new_pixbuf="" +list_labels = [] +label_limits = [] +print_limits = [] +#label_selected = "" +BLANK_W = 1134 +BLANK_H = 496 + + +def get_resource_path(rel_path): + dir_of_py_file = os.path.dirname(__file__) + rel_path_to_resource = os.path.join(dir_of_py_file, rel_path) + abs_path_to_resource = os.path.abspath(rel_path_to_resource) + return abs_path_to_resource + +class DialogCompositor(Gtk.Dialog): + def __init__(self, parent): + global new_pixbuf + Gtk.Dialog.__init__(self, title="Kompozytor", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_SAVE, + Gtk.ResponseType.OK, + ) + self.compos = Gtk.Grid() + file_img_composite=get_resource_path("/usr/share/postagelabels/blank_small.png") + new_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_composite, + width=500, + height=250, + preserve_aspect_ratio=True) + + self.add(self.compos) + + self.compos.set_row_spacing(10) + self.compos.set_column_spacing(10) + #self.compos.set_column_homogeneous(True) # rozszerza kontrolke na resztę okna + + # potrzebne do wyśwetlenia tekstu + self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, new_pixbuf.get_width(), new_pixbuf.get_height()) + self.context = cairo.Context(self.surface) + + Gdk.cairo_set_source_pixbuf(self.context, new_pixbuf, 0, 0) + self.context.paint() #paint the pixbuf + + self.fontsize= 25 + + box = self.get_content_area() + + self.com_first = Gtk.Image.new_from_pixbuf(new_pixbuf) + self.compos.attach(self.com_first,0,0,1,3) + + + self.entry1 = Gtk.Entry() + self.entry1.set_text("Hello World") + self.compos.attach(self.entry1,1,0,1,1) + + + self.entry2 = Gtk.Entry() + self.entry2.set_text("Hello World") + self.compos.attach(self.entry2,1,1,1,1) + + self.entry3 = Gtk.Entry() + self.entry3.set_text("Hello World") + self.compos.attach(self.entry3,1,2,1,1) + + self.entry4 = Gtk.Entry() + self.entry4.set_text("Hello World") + self.compos.attach(self.entry4,1,3,1,1) + + button1 = Gtk.Button.new_with_label("Aktualizuj") + button1.connect("clicked", self.actualization1) + self.compos.attach(button1,2,0,1,1) + + button2 = Gtk.Button.new_with_label("Aktualizuj") + button2.connect("clicked", self.actualization2) + self.compos.attach(button2,2,1,1,1) + + button3 = Gtk.Button.new_with_label("Aktualizuj") + button3.connect("clicked", self.actualization3) + self.compos.attach(button3,2,2,1,1) + + button4 = Gtk.Button.new_with_label("Aktualizuj") + button4.connect("clicked", self.actualization4) + self.compos.attach(button4,2,3,1,1) + + + button_clear = Gtk.Button.new_with_label("Wyczyść") + button_clear.connect("clicked", self.entry_clear) + self.compos.attach(button_clear,3,0,1,4) + box.add(self.compos) + + self.show_all() + + def entry_clear(self, button): + global new_pixbuf + file_img_selected=get_resource_path("/usr/share/postagelabels/blank_small.png") + new_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_selected, + width=500, + height=250, + preserve_aspect_ratio=True) + + + if self.com_first != None: + self.compos.remove(self.com_first) + self.com_first=None + else: + self.compos.remove(self.com_new) + + # czyści etykiete + self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, new_pixbuf.get_width(), new_pixbuf.get_height()) + self.context = cairo.Context(self.surface) + Gdk.cairo_set_source_pixbuf(self.context, new_pixbuf, 0, 0) + self.context.paint() #paint the pixbuf + + + self.com_new = Gtk.Image.new_from_pixbuf(new_pixbuf) + self.compos.attach(self.com_new,0,0,1,3) + + self.show_all() + + + def actualization1(self, button): + global new_pixbuf + self.context.move_to(15, 20+self.fontsize) + self.context.set_font_size(self.fontsize) + self.context.set_source_rgba(0,0,0,1) + self.context.show_text(self.entry1.get_text()) + + #get the resulting pixbuf + self.surface= self.context.get_target() + new_pixbuf= Gdk.pixbuf_get_from_surface(self.surface, 0, 0, self.surface.get_width(), self.surface.get_height()) + + if self.com_first != None: + self.compos.remove(self.com_first) + self.com_first=None + else: + self.compos.remove(self.com_new) + + + self.com_new = Gtk.Image.new_from_pixbuf(new_pixbuf) + self.compos.attach(self.com_new,0,0,1,3) + + self.show_all() + + def actualization2(self, button): + global new_pixbuf + self.context.move_to(15, 20+self.fontsize+self.fontsize+15) + self.context.set_font_size(self.fontsize) + self.context.set_source_rgba(0,0,0,1) + self.context.show_text(self.entry2.get_text()) + + #get the resulting pixbuf + self.surface= self.context.get_target() + new_pixbuf= Gdk.pixbuf_get_from_surface(self.surface, 0, 0, self.surface.get_width(), self.surface.get_height()) + + if self.com_first != None: + self.compos.remove(self.com_first) + self.com_first=None + else: + self.compos.remove(self.com_new) + + + self.com_new = Gtk.Image.new_from_pixbuf(new_pixbuf) + self.compos.attach(self.com_new,0,0,1,3) + + self.show_all() + def actualization3(self, button): + global new_pixbuf + self.context.move_to(15, 20+self.fontsize+self.fontsize+self.fontsize+30) + self.context.set_font_size(self.fontsize) + self.context.set_source_rgba(0,0,0,1) + self.context.show_text(self.entry3.get_text()) + + #get the resulting pixbuf + self.surface= self.context.get_target() + new_pixbuf= Gdk.pixbuf_get_from_surface(self.surface, 0, 0, self.surface.get_width(), self.surface.get_height()) + + if self.com_first != None: + self.compos.remove(self.com_first) + self.com_first=None + else: + self.compos.remove(self.com_new) + + + self.com_new = Gtk.Image.new_from_pixbuf(new_pixbuf) + self.compos.attach(self.com_new,0,0,1,3) + + self.show_all() + + def actualization4(self, button): + global new_pixbuf + self.context.move_to(15, 20+self.fontsize+self.fontsize+self.fontsize+self.fontsize+50) + self.context.set_font_size(self.fontsize) + self.context.set_source_rgba(0,0,0,1) + self.context.show_text(self.entry4.get_text()) + + #get the resulting pixbuf + self.surface= self.context.get_target() + new_pixbuf= Gdk.pixbuf_get_from_surface(self.surface, 0, 0, self.surface.get_width(), self.surface.get_height()) + + if self.com_first != None: + self.compos.remove(self.com_first) + self.com_first=None + else: + self.compos.remove(self.com_new) + + + self.com_new = Gtk.Image.new_from_pixbuf(new_pixbuf) + self.compos.attach(self.com_new,0,0,1,3) + + self.show_all() + + +class DialogHelp(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="Pomoc", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + + self.set_default_size(150, 100) + label1 = Gtk.Label(label="Aplikacja służąca do aranżacji wydruku etykiet pocztowych.\nMożna zrówno tworzyć jak i wczytywać gotowe etykiety,\n a następnie umieszczać jena siatce.\nAplikacja bierze poprawkę dla drukarki Epson serii L,\nz ramką wokół wydruku.\n") + label2 = Gtk.Label(label="Pojedyńcza etykieta powinna mieć wymiary: 1134x496 px.\nKomptabilny papier pochodzi z zasobów \'Galerii Papieru\'\n\nNa siatce etykiete dodajemy \'lpm\', a usuwamy \'ppm\'.\n\nAplikacja na ekranach HD Ready będzie zachowywać się\nniestabilnie! Zalecany ekran to Full HD.") + box = self.get_content_area() + box.add(label1) + box.add(label2) + self.show_all() + +class DialogAbout(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, title="O Programie", transient_for=parent, flags=0) + self.props.border_width = 20 + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + ) + + self.set_default_size(150, 100) + + label = Gtk.Label(label="\tAplikacja napisana na podstawie:\n\nhttps://python-gtk-3-tutorial.readthedocs.io/\n\n\t\tWersja: 0.220120-2\n\n\t\tPrzemysław R. Pietraszczyk\n\n\t\t\tWrzesień 2021\n\t\t\tLicencja: GPL\n\n") + + box = self.get_content_area() + box.add(label) + + button = Gtk.LinkButton("http://www.prp.xlx.pl", label="Strona Domowa") + box.add(button) + + self.show_all() + + + + +class DialogMessage(Gtk.Dialog): + def __init__(self, parent): + super().__init__(title="Błąd zaznaczenia", transient_for=parent, flags=0) + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.OK + + ) + + self.set_default_size(150, 100) + + label = Gtk.Label(label="Powinieneś najperw zaznaczyć etykiete,\nz listy dostępnych etykiet.") + + box = self.get_content_area() + box.add(label) + self.show_all() + + + + +class FileChooserIMG(Gtk.Window): + def __init__(self): + Gtk.Window.__init__(self, title="Wybór plików graficznych") + global file_img_selected + + dialog = Gtk.FileChooserDialog(title="Please choose a file", parent=self, action=Gtk.FileChooserAction.OPEN) + dialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, + ) + + self.add_filters(dialog) + + response = dialog.run() + if response == Gtk.ResponseType.OK: + print("Open clicked") + print("File selected: " + dialog.get_filename()) + file_img_selected=dialog.get_filename() + list_labels.append(file_img_selected) + elif response == Gtk.ResponseType.CANCEL: + print("Cancel clicked") + + dialog.destroy() + + def add_filters(self, dialog): + filter_png = Gtk.FileFilter() + filter_png.set_name("Pliki PNG") + filter_png.add_mime_type("image/png") + dialog.add_filter(filter_png) + + filter_jpeg = Gtk.FileFilter() + filter_jpeg.set_name("Pliki JPEG") + filter_jpeg.add_mime_type("image/jpeg") + dialog.add_filter(filter_jpeg) + + + + +class FileChooserLabel(Gtk.Window): + def __init__(self, this): + global glob_liststore + Gtk.Window.__init__(self, title="Zapis etykiety") + global file_label_save + + dialog = Gtk.FileChooserDialog(title="Please choose a file", parent=self, action=Gtk.FileChooserAction.SAVE) + dialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_SAVE, + Gtk.ResponseType.OK, + ) + + self.add_filters(dialog) + + response = dialog.run() + if response == Gtk.ResponseType.OK: + print("Open clicked") + print("File selected: " + dialog.get_filename()) + file_label_save=dialog.get_filename() + pixbuf2 = new_pixbuf.scale_simple(1134, 496, GdkPixbuf.InterpType.BILINEAR) + pixbuf2.savev(file_label_save,"png", ["quality"], ["100"]) + this.available_labels(file_label_save) + + + elif response == Gtk.ResponseType.CANCEL: + print("Cancel clicked") + + dialog.destroy() + + def add_filters(self, dialog): + filter_png = Gtk.FileFilter() + filter_png.set_name("Pliki PNG") + filter_png.add_mime_type("image/png") + dialog.add_filter(filter_png) + """ + filter_jpeg = Gtk.FileFilter() + filter_jpeg.set_name("Pliki JPEG") + filter_jpeg.add_mime_type("image/jpeg") + dialog.add_filter(filter_jpeg) + """ + + + + + + + +class MyWindow(Gtk.Window): + + def __init__(self): + super().__init__() + + self.init_ui() + + + def refresh_label_view(self): + + if self.image2 != None: + grid.remove(self.image2) + self.image2=None + else: + grid.remove(self.image_new) + + + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=self.label_selected, + width=500, + height=250, + preserve_aspect_ratio=True) + + self.image_new = Gtk.Image.new_from_pixbuf(pixbuf) + grid.attach(self.image_new,0,1,1,1) + + self.show_all() + + def on_menu_file_new_template(self, widget): + + n = 0 + file_img_selected=get_resource_path("/usr/share/postagelabels/blank_small.png") + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_selected, + width=250, + height=117, #125, + preserve_aspect_ratio=True) + + + for l in label_limits: + + print ("IMAGE2: "+str(pixbuf)) + if pixbuf is not None: + self.image_template[n].set_from_pixbuf(pixbuf) + print_limits[n][2] = None + + + + n += 1 + self.show_all() # najprawdopodobnie wymagane po pakietowaniu + + def on_menu_file_load_img_generic(self, widget): + global grid + filename=FileChooserIMG() + + if len(file_img_selected) != 0: + + if self.image2 != None: + grid.remove(self.image2) + self.image2=None + else: + grid.remove(self.image_new) + + + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_selected, + width=500, + height=250, + preserve_aspect_ratio=True) + + self.image_new = Gtk.Image.new_from_pixbuf(pixbuf) + + grid.attach(self.image_new,0,1,1,1) + + + self.scrolled_window.remove(self.treeview) + grid.remove(self.scrolled_window) + self.liststore = Gtk.ListStore(str, str) + + self.treeview.remove_column(self.column_text) + + for o in list_labels: + self.liststore.append([o,""]) + + self.scrolled_window = Gtk.ScrolledWindow () + self.scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + + self.treeview = Gtk.TreeView(model=self.liststore) + + self.renderer_text = Gtk.CellRendererText() + self.column_text = Gtk.TreeViewColumn("Dostępne etykiety", self.renderer_text, text=0) + self.treeview.append_column(self.column_text) + + self.treeview.connect("button_release_event", self.mouse_selected) + + self.scrolled_window.add (self.treeview) + grid.attach(self.scrolled_window,0,2,1,6) + + self.show_all() + + def on_menu_composite_generic(self, widget): + dialog = DialogCompositor(self) + response = dialog.run() + + if response == Gtk.ResponseType.OK: + print("Save clicked") + dialog_file_label=FileChooserLabel(self) + elif response == Gtk.ResponseType.CANCEL: + print("Cancel clicked") + + dialog.destroy() + + def on_menu_file_quit(self, widget): + Gtk.main_quit() + + def on_menu_help(self, widget): + dialog = DialogHelp(self) + response = dialog.run() + + dialog.destroy() + + def on_menu_about(self, widget): + dialog = DialogAbout(self) + response = dialog.run() + + dialog.destroy() + + + def add_info_menu_actions(self, action_group): + action_info_menu = Gtk.Action(name="InfoMenu", label="Info") + action_group.add_action(action_info_menu) + + action_new = Gtk.Action( + name="Help", + label="Pomoc", + tooltip="Create a new file", + ) + action_new.connect("activate", self.on_menu_help) + action_group.add_action_with_accel(action_new, None) + + action_new = Gtk.Action( + name="About", + label="O Programie", + tooltip="Create a new file", + ) + action_new.connect("activate", self.on_menu_about) + action_group.add_action_with_accel(action_new, None) + + def add_file_menu_actions(self, action_group): + action_filemenu = Gtk.Action(name="FileMenu", label="Plik") + action_group.add_action(action_filemenu) + + action_fileloadmenu = Gtk.Action(name="FileLoad", stock_id=Gtk.STOCK_NEW) + action_group.add_action(action_fileloadmenu) + + action_new = Gtk.Action( + name="LoadImg", + label="Załaduj etykiete", + tooltip="Wczytuje etykiete", + ) + action_new.connect("activate", self.on_menu_file_load_img_generic) + action_group.add_action_with_accel(action_new, None) + + + action_new = Gtk.Action( + name="NewTemplate", + label="Nowy szablon", + tooltip="Tworzy szablon na nowo", + ) + action_new.connect("activate", self.on_menu_file_new_template) + action_group.add_action_with_accel(action_new, None) + + + action_new = Gtk.Action( + name="Composite", + label="Kompozytor", + tooltip="Tworzy nową etykiete", + ) + action_new.connect("activate", self.on_menu_composite_generic) + action_group.add_action_with_accel(action_new, None) + + + action_print = Gtk.Action(name="FilePrint", stock_id=Gtk.STOCK_PRINT) + action_print.connect("activate", self.print_image) + action_group.add_action(action_print) + + + action_filequit = Gtk.Action(name="FileQuit", stock_id=Gtk.STOCK_QUIT) + action_filequit.connect("activate", self.on_menu_file_quit) + action_group.add_action(action_filequit) + + + + # tworzy menu bar + def create_ui_manager(self): + uimanager = Gtk.UIManager() + + # Throws exception if something went wrong + uimanager.add_ui_from_string(UI_INFO) + + # Add the accelerator group to the toplevel window + accelgroup = uimanager.get_accel_group() + self.add_accel_group(accelgroup) + return uimanager + + + + def mouse_selected(self, tv, event): + + if event.button == 1: + + # Begin added code + pthinfo = self.treeview.get_path_at_pos(event.x, event.y) + if pthinfo != None: + path,col,cellx,celly = pthinfo + self.treeview.grab_focus() + self.treeview.set_cursor(path,col,0) + # End added code + + selection = self.treeview.get_selection() + (model, iter) = selection.get_selected() + print(model[iter][0]) + self.label_selected = model[iter][0] + + self.refresh_label_view() + + + def onclick_in_pixbuf (self, box, event): + if event.button == 1: + try: + print ("\nKliknieto na pozycji") + print (event.x, event.y) + print ("\n") + e = [int(event.x), int(event.y)] + n = 0 + + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=self.label_selected, + width=250, + height=117, #125, + preserve_aspect_ratio=True) + + + for l in label_limits: + + x, y = l[:] + e_x, e_y = e[:] + + if e_x >= x and e_y >= y and e_x <= x+250 and e_y <= y+117: + if pixbuf is not None: + self.image_template[n].set_from_pixbuf(pixbuf) + print_limits[n][2] = self.label_selected + + break + n += 1 + except AttributeError: + + dialog = DialogMessage(self) + response = dialog.run() + + + dialog.destroy() + if event.button == 3: + + + e = [int(event.x), int(event.y)] + n = 0 + + file_img_selected=get_resource_path("/usr/share/postagelabels/blank_small.png") + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_selected, + width=250, + height=117, #125, + preserve_aspect_ratio=True) + + + for l in label_limits: + + x, y = l[:] + e_x, e_y = e[:] + + if e_x >= x and e_y >= y and e_x <= x+250 and e_y <= y+117: + print ("IMAGE2: "+str(pixbuf)) + if pixbuf is not None: + self.image_template[n].set_from_pixbuf(pixbuf) + print_limits[n][2] = None + + + break + n += 1 + self.show_all() # najprawdopodobnie wymagane po pakietowaniu + + def on_mouse_move_in_pixbuf (self, box, event): + e = [int(event.x), int(event.y)] + n = 0 + for l in label_limits: + + x, y = l[:] + e_x, e_y = e[:] + + if e_x >= x and e_y >= y and e_x <= x+200 and e_y <= y+92: + pass + + # dnae dla wydruku + def set_print_limits(self): + start_x = 0 + start_y = 0 + end_x = BLANK_W * 2 #1234*2 + end_y = BLANK_H * 6 #3508 + cols = 2 + rows = 6 + width = BLANK_W #1234 + height = BLANK_H #585 + + for j in range (start_y, end_y, height): + for i in range (start_x, end_x, width): + print_limits.append([i, j, None]) + + print ("PRINT LIMIST") + print (print_limits) + + + # dane dla siatki + def set_label_limits(self): + start_x = 0 + start_y = 0 + end_x = 500 + end_y = 104 * 6 #117 * 6 #700 + cols = 2 + rows = 6 + width = 250 + height = 104 #117 + + for j in range (start_y, end_y, height): + for i in range (start_x, end_x, width): + label_limits.append([i, j]) + + + def print_page(self, operation=None, context=None, page_nr=None): + + ctx = context.get_cairo_context() + + # make cairo ImageSurface from the png file + surface = cairo.ImageSurface.create_from_png('/tmp/postagelabels.png') + + #ctx.set_operator(cairo.OPERATOR_SOURCE) + ctx.set_source_surface(surface) + ctx.paint () + #ctx.set_operator(cairo.OPERATOR_OVER) + os.remove("/tmp/postagelabels.png"); + + + + #page = self.doc.get_page(page_nr) + #page.render(ctx) + + + + + + def print_image(self, widget): + print ("PRINT LIMITS") + print (print_limits) + + page_width = 210 + page_height = 297 + #page_margin_top = 20 + dpi = 600 + #_mm_dpi = 72 / 25.4 + + + + + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size('/usr/share/postagelabels/szablon.png', 2480, 3508) + + for i in range (12): + if print_limits[i][2] != None: + + if i%2 == 1: + center_beam = 24 + else: + center_beam = 0 + + label = GdkPixbuf.Pixbuf.new_from_file_at_size(print_limits[i][2], BLANK_W, BLANK_H) + #self.freeze_child_notify() + label.composite(pixbuf, print_limits[i][0]+89+center_beam, print_limits[i][1]+237, label.get_width(), label.get_height(), print_limits[i][0]+89+center_beam, print_limits[i][1]+237, 1, 1, GdkPixbuf.InterpType.NEAREST, 255) + #self.thaw_child_notify() + + # z orginalnymi wielkościami nie chce drukowac + pixbuf2 = pixbuf.scale_simple(600, 855, GdkPixbuf.InterpType.BILINEAR) + + pixbuf2.savev("/tmp/postagelabels.png","png", ["quality"], ["100"]) + """ + paper_size = Gtk.PaperSize.new_custom("1.0x", "2480 x 3508", 2480, 3508, Gtk.Unit.POINTS) + + page_setup = Gtk.PageSetup() + page_setup.set_paper_size(paper_size) + pd = Gtk.PrintOperation() + #pd.set_embed_page_setup(True) + pd.set_default_page_setup(page_setup) + #pd.set_unit(Gtk.Unit.POINTS) + pd.set_n_pages(1) + pd.connect("draw_page", self.print_page) + result = pd.run(Gtk.PrintOperationAction.PRINT_DIALOG, None) # window zamisat None + print(result) # handle errors etc. + """ + + + #ps= Gtk.PaperSize.new_custom("1.0x", "2480 x 3508", 2480, 3508, Gtk.Unit.POINTS) + + ps = Gtk.PaperSize.new_custom("1.0x", "cc", page_width, page_height, Gtk.Unit.MM) + print_settings = Gtk.PrintSettings() + print_settings.set_resolution(dpi) + + page_setup = Gtk.PageSetup() + page_setup.set_paper_size(ps) + page_setup.set_bottom_margin(0.0, Gtk.Unit.MM) + page_setup.set_left_margin(0.0, Gtk.Unit.MM) + page_setup.set_right_margin(0.0, Gtk.Unit.MM) + page_setup.set_top_margin(0.0, Gtk.Unit.MM) + page_setup.set_orientation(Gtk.PageOrientation.PORTRAIT) + + + print_operation = Gtk.PrintOperation() + print_operation.set_n_pages(1) + print_operation.set_default_page_setup(page_setup) + print_operation.set_print_settings(print_settings) + print_operation.connect("draw_page", self.print_page) + #print_operation.set_export_filename("example.pdf") + + result = print_operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, None) # window zamisat None + + #result = print_operation.run(Gtk.PrintOperationAction.PREVIEW, None) + print(result) + + def available_labels(self, name): + + self.scrolled_window.remove(self.treeview) + grid.remove(self.scrolled_window) + self.liststore = Gtk.ListStore(str, str) + + self.treeview.remove_column(self.column_text) + + list_labels.append(name) + for o in list_labels: + self.liststore.append([o,""]) + + self.scrolled_window = Gtk.ScrolledWindow () + self.scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + + self.treeview = Gtk.TreeView(model=self.liststore) + + self.renderer_text = Gtk.CellRendererText() + self.column_text = Gtk.TreeViewColumn("Dostępne etykiety", self.renderer_text, text=0) + self.treeview.append_column(self.column_text) + + self.treeview.connect("button_release_event", self.mouse_selected) + + self.scrolled_window.add (self.treeview) + grid.attach(self.scrolled_window,0,2,1,6) + + self.show_all() + + + def init_ui(self): + global grid + + + + self.image_new=None + self.props.border_width = 20 + + self.add(grid) + + grid.set_row_spacing(10) + grid.set_column_spacing(10) + grid.set_column_homogeneous(True) # rozszerza kontrolke z utworami na resztę okna + + action_group = Gtk.ActionGroup(name="my_actions") + + self.add_file_menu_actions(action_group) + self.add_info_menu_actions(action_group) + + uimanager = self.create_ui_manager() + uimanager.insert_action_group(action_group) + + menubar = uimanager.get_widget("/MenuBar") + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + box.pack_start(menubar, False, False, 0) + + + grid.attach(box, 0, 0, 2, 1) + + + + event_box = Gtk.EventBox () + event_box.connect ('button-press-event', self.onclick_in_pixbuf) + event_box.connect("motion-notify-event", self.on_mouse_move_in_pixbuf) + event_box.add_events(Gdk.EventMask.POINTER_MOTION_MASK) + + self.set_label_limits() + self.set_print_limits() + frame = Gtk.Frame() + + + file_img_selected=get_resource_path("/usr/share/postagelabels/blank_small.png") + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_selected, + width=250, + height=117, #125 + preserve_aspect_ratio=True) + + + self.all_img_template = Gtk.Grid() + + self.image_template = [] + self.image_template= [0 for i in range(12)] + + + left = 0 + top = 1 + top_next=False + multiplier = 0 + n_multi = 1; + first = False + n = 2 + for i in range(12): + print ("left: "+str(left)+" top: "+str(top)) + + if left == 2: + left = 0; + n_multi += 1 + print ("multi: " +str(n_multi)+" %"+ str(n_multi%4)) + if n_multi%2== 0: + if first == False: + multiplier = 1 + first = True + multiplier += n + n *= 2 + self.image_template[i] = Gtk.Image.new_from_pixbuf(pixbuf) + self.all_img_template.attach(self.image_template[i], left, top+multiplier, 1 , top+multiplier) + left += 1 + if top_next == False: + top_next = True + pass + else: + top_next = False + top +=1 + + event_box.add(self.all_img_template) + + + frame.add(event_box) + grid.attach(frame,1,1,1,6) + + + + # powiększenie etykiety po lewej stronie + file_img_selected=get_resource_path("/usr/share/postagelabels/blank_small.png") + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( + filename=file_img_selected, + width=500, + height=250, + preserve_aspect_ratio=True) + + self.image2 = Gtk.Image.new_from_pixbuf(pixbuf) + + grid.attach(self.image2,0,1,1,1) + + + + self.liststore = Gtk.ListStore(str, str) + self.treeview = Gtk.TreeView(model=self.liststore) + self.treeview.connect("button_release_event", self.mouse_selected) + + + + self.renderer_text = Gtk.CellRendererText() + self.column_text = Gtk.TreeViewColumn("Dostępne etykiety", self.renderer_text, text=0) + self.treeview.append_column(self.column_text) + + self.scrolled_window = Gtk.ScrolledWindow () + self.scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + + self.scrolled_window.add (self.treeview) + grid.attach(self.scrolled_window,0,2,1,6) + + + + + self.set_border_width(10) + self.set_title("Naklejki Pocztowe") + self.set_default_size(1000, 655) + self.connect("destroy", Gtk.main_quit) + + + +win = MyWindow() +win.show_all() +Gtk.main() + + + + + + + + diff -r 000000000000 -r dcd610585610 postagelabels/xcf/blank_small.xcf Binary file postagelabels/xcf/blank_small.xcf has changed diff -r 000000000000 -r dcd610585610 postagelabels/xcf/labelpost.xcf Binary file postagelabels/xcf/labelpost.xcf has changed diff -r 000000000000 -r dcd610585610 verysimpleslideshow/COPYRIGHT --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/COPYRIGHT Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,27 @@ +This software is distributed under the Public Domain. Since it is +not enough anymore to tell people: 'hey, just do with it whatever +you like to do', you can consider this software being distributed +under the CC0 Public Domain Dedication +(http://creativecommons.org/publicdomain/zero/1.0/legalcode.txt). + +In cases, where the law prohibits the recognition of Public Domain +software, this software can be licensed under the zlib license as +stated below: + +Copyright (C) 2023 Przemyslaw R. Pietraszczyk + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgement in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff -r 000000000000 -r dcd610585610 verysimpleslideshow/Changelog --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/Changelog Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,17 @@ +0.230913-0 - Dołąćzenie starszego resamplingu: ANTIALIAS obok nowego LANCZOS, plus drobne poprawki +0.230910-1 - Rozciągnięcie filmu w czasie, teraz pauza pomiędzy klatkami jest całkiem spora. ( parametr -r 0.2 - ffmpeg) +0.230910-0 - Jawne wariacje z atrybutem Resampling.LANCZOS funkcji resize() - czy skrypt ciągle zjada klatki ? +0.230909-1 - Rozpoznaje proporcje zdjęcia i sam decyduje czy wczytać fotografie - działa z błędami +0.230908-1 - Próba spowolnienia filmu argumentem ffmpeg - niewiele pomogło +0.230907-0 - Próba dostosowania skryptu do Okienek - zmiana slash na unslash w path dla list.txt - bez powodzenia +0.230906-8 - Tworzy film, bez czarnych klatek. ffmpeg generuje dziwny błąd o nieodnalezionej klatce... +0.230906-7 - Przetwarza na podstawie pliku, dodaje czarne klatki +0.230906-6 - Próba przetwarzania fotografii na podstawie pliku list.txt dla ffmpeg +0.230905-2 - Próba przetwarzania w bieżącym katalogu (czasowo) +0.230905-1 - Ciąg dalszy integracji z Okienkami +0.230905-0 - Pierwsze przymiarki do komptabilności z Okienkami +0.230903-3 - Nie gubi już zdjęć, i nie dodaje czarnych klatek +0.230903-2 - Automatycznie skaluje obrazy do 1080 px dla najdłuższego boku +0.230903-1 - Skrypt tworzy zarówno video portretowe jak i krajobrazowe +0.230902-0 - Inicjacja projektu + diff -r 000000000000 -r dcd610585610 verysimpleslideshow/CzytajTo --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/CzytajTo Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,23 @@ +Very Simple Slide Show (vsss) + +Skrypt Pythona służący do tworzenia pokazów slajdów ze zdjęć JPG. +Aplikacja umożliwia tworzenie filmików ze zdjęć, zarówno w orientacji pionowej jak i poziomej. Powstała z osobistych potrzeb autora. Mając na celu, ułatwienie tworzenia pokazów slajdów na potrzeby Facebooka. Intencją autora nie było tworzenie aplikacji dla wszystkich, pisząc ten skrypt, skupił się w głównej mierze na własnych potrzebach i możliwościach. + +Skrypt pracuje ze zdjęciami o wielkości 1080 pikseli dla najdłuższego boku. Jeśli obraz jest większy, zostanie wówczas przeskalowany. +Dla filmu o orientacji pionowej, zdjęcia horyzontalne, zostaną dodane w parze, jedno pod drugim. Dla obrazu nieparzystego, przewidziane jest umieszczenie fotografii pośrodku kadru. Dla orientacji poziomej filmu, wice wersa. + +Program współpracuje z fotografiami o proporcjach 3:2 oraz 4:3. Co jest w zupełności wystarczające dla autora, rozszerzenie o formaty panoramiczne, nie jest przewidziane. Program sam rozpoznaje format zdjęcia – choć potrafi nieoczekiwanie odrzucić to i owo. Do przetwarzania obrazu, skrypt wykorzystuje bibliotekę PILLOW. Natomiast do kodowania filmu, polecenie ‘ffmpeg’. W przypadku Fedory, z której korzysta autor. Wykorzystywany pakiet to ‘ffmpeg’ z RPM Fusion. Argumenty z tej wersji polecenia, mogą być niezgodne z wersją ‘ffmpeg-free’ z repozytorium Fedora Core, co trzeba mieć na uwadze. Wykorzystywany kodek to x264 (zamiast openh264 z FC). Więc jeśli używasz ‘ffmpeg-free’ i chciałbyś używać tego skryptu, to mogę Tobie polecić tą stronę, zawierającą informacje o zmianie wersji pakietu: https://rpmfusion.org/Howto/Multimedia +Skrypt został w ostatnim czasie, przystosowany do pracy w dystrybucjach Debian/Ubuntu. Testowany był w Lubuntu 22.04 ze standardową wersją ‘ffmpeg’. W przypadku Okienek, testy nie przebiegły pomyślnie. + +Instalacja skryptu z archiwum źródlowego: + sudo make install +Deinstalacja: + sudo make uninstall + +Użyj skryptu zainstalowanego w systemie plików, w katalogu ze zdjęciami, poleceniem: +‘vsss -H’ lub ‘vsss’ – utworzenie filmu horyzontalnego +‘vsss -V’ – utworzenie filmu wertykalnego +‘vsss -h’ – wyświetlenie pomocy oraz numeru wersji. + +Z pozdrowieniami: +Przemysław R. Pietraszczyk diff -r 000000000000 -r dcd610585610 verysimpleslideshow/DEBIAN/HOWTO.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/DEBIAN/HOWTO.txt Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,2 @@ +foramt zapisu nazwy pliku: + photocrop_0.221225-0~alpha.tar.gz diff -r 000000000000 -r dcd610585610 verysimpleslideshow/DEBIAN/debian.changelog --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/DEBIAN/debian.changelog Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,18 @@ +verysimpleslideshow (0.230909-1) unstable; urgency=low + + * Last release + + -- Przemysław R. Pietraszczyk Thu, 09 Sep 2023 22:47:00 +0200 + + +verysimpleslideshow (0.230907-0) unstable; urgency=low + + * Last release + + -- Przemysław R. Pietraszczyk Thu, 07 Sep 2023 19:30:00 +0200 + +verysimpleslideshow (0.230903-3) unstable; urgency=low + + * Last release + + -- Przemysław R. Pietraszczyk Sun, 03 Sep 2023 21:21:21 +0200 diff -r 000000000000 -r dcd610585610 verysimpleslideshow/DEBIAN/debian.control --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/DEBIAN/debian.control Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,13 @@ +Source: verysimpleslideshow +Section: python +Priority: extra +Maintainer: Przemysław R. Pietraszczyk +Build-Depends: debhelper-compat (= 12), python3-all, python-setuptools, dh-python, fakeroot +Standards-Version: 0.230910-1 +Homepage: http://prymula.ct8.pl + + +Package: verysimpleslideshow +Architecture: all +Depends: ${misc:Depends}, python3-all, ffmpeg +Description: CLI script for creating slideshows diff -r 000000000000 -r dcd610585610 verysimpleslideshow/DEBIAN/debian.postinst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/DEBIAN/debian.postinst Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,7 @@ +#!/bin/sh +set -e + +chmod 0755 /usr/bin/verysimpleslideshow.py +ln -s /usr/bin/verysimpleslideshow.py /usr/bin/vsss + +exit 0 diff -r 000000000000 -r dcd610585610 verysimpleslideshow/DEBIAN/debian.postrm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/DEBIAN/debian.postrm Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +case "$1" in + 0) # last one out put out the lights + rm -f /usr/bin/vsss + ;; +esac + +exit 0 diff -r 000000000000 -r dcd610585610 verysimpleslideshow/DEBIAN/debian.rules --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/DEBIAN/debian.rules Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,19 @@ +#!/usr/bin/make -f + +export DH_VERBOSE = 1 + +clean: + @ + +build: + @ + +binary: + mkdir -pm 0755 debian/verysimpleslideshow + mkdir -pm 0755 debian/verysimpleslideshow/usr/bin + mkdir -pm 0755 debian/verysimpleslideshow/usr/share + mkdir -pm 0755 debian/verysimpleslideshow/usr/share/verysimpleslideshow + cp verysimpleslideshow.py debian/verysimpleslideshow/usr/bin/ + cp CzytajTo debian/verysimpleslideshow/usr/share/verysimpleslideshow/ + dh_gencontrol + dh_builddeb diff -r 000000000000 -r dcd610585610 verysimpleslideshow/DEBIAN/format.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/DEBIAN/format.sh Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,2 @@ +mkdir exec +exec /usr/lib/build/debtransform ./ ./verysimpleslideshow.dsc ./exec diff -r 000000000000 -r dcd610585610 verysimpleslideshow/DEBIAN/verysimpleslideshow.dsc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/DEBIAN/verysimpleslideshow.dsc Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,40 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +Format: 1.0 +Source: verysimpleslideshow +Binary: verysimpleslideshow +Architecture: all +Version: 0.230910-1 +Maintainer: Przemysław R. Pietraszczyk +Homepage: http://prymula.ct8.pl +DEBTRANSFORM-TAR: verysimpleslideshow-0.230910-1.tar.gz + +Standards-Version: 0.230910-1 +Build-Depends: debhelper-compat (>= 12), python3-all, python-setuptools, dh-python, fakeroot +Package-List: + verysimpleslideshow deb x11 optional arch=all +Files: + b31649bc23b2b7bca9ab70ea3410711a 128820 verysimpleslideshow_0.230910-1.tar.gz + 29e7e7e60bc81891e3da3871b9eeb549 764 verysimpleslideshow-0.230910-1.debian.tar.xz + +-----BEGIN PGP SIGNATURE----- + +mQENBGEf/c4BCAC2d4ymW0pRZV36qLtlt/WGb83kos5UX5UbwvkQrbUjAbOPOY8w +DT3M1neYOAPZx38924aPTFKbZPcz+rK/7Wcv1kzgfux5zXQJTLeqpkhUYAgXUg2p +oK9ZXsai82fOicTrClOyJLLYQ8C1vj8yUh7e3ERljEyp5Nxg/lY92rwcZ4WYR193 +nGDInlDl5JlWUcLlk/RFnC5bB+T6ZZr5FBX/eDnKAPEl+N3MWpDs2JLDy7EUPhcG +U/60X0wuXHPTwMoNbB3ep/bWRxwEej0sFad5GXeCh7hKCroi/kLmLTDIxSD8lsRr +Y4H/8H2lBBsQq7bRL88N6ZDGCocPo11V83kFABEBAAG0NmhvbWU6cHJ6ZW0gT0JT +IFByb2plY3QgPGhvbWU6cHJ6ZW1AYnVpbGQub3BlbnN1c2Uub3JnPokBPgQTAQgA +KAUCYR/9zgIbAwUJBB6wAAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQcXEc +NPcNefFEAAf+Kg4cs/C99kEIMv/PO85VW+p8rcTwCViOFUX5WrBhwsRyPAKrJPPG +KD6PdnvoplcNQwhQ8nRsRiiaiUtPcj8Bvi6EdPgSgXXObKKJtCyaAa1rURdlb9iW +CfvXA6oW+PGlii9BMnXOAtkeFIzZooO9oXd+wez66P+4VWFiFzxanLfLn/hwbWxQ +68rlO3QNGbrMeODBgwh9s83XGmAg47mn2fldTAmTbJDf9tMi1LhX2MmLyN9wjqsd +4IZ9JMri2JTnIhYfxWdKT9gsu7q0g8LqonaXMvlOEyHFRF1YQ0i7G6fFbvDcKCZN +W1Eja55FdN7smv5Eck6UTIIYyWPuAv45uohGBBMRAgAGBQJhH/3PAAoJEDswEbdr +nWUj4M0AniJ389dyvUjjgS91AcOb08E4moV9AJ9WDL3/7GYYUL/Azw43QucWPnXr +uw== +=+lfH +-----END PGP SIGNATURE----- diff -r 000000000000 -r dcd610585610 verysimpleslideshow/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/Makefile Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,15 @@ + +prefix=/usr/local + +all: + +install: + install -m 0755 verysimpleslideshow.py $(prefix)/bin + ln -s $(prefix)/bin/verysimpleslideshow.py $(prefix)/bin/vsss + + +uninstall: + rm $(prefix)/bin/vsss + rm $(prefix)/bin/verysimpleslideshow.py + +.PHONY: all install uninstall diff -r 000000000000 -r dcd610585610 verysimpleslideshow/RPM/verysimpleslideshow.spec --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/RPM/verysimpleslideshow.spec Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,72 @@ +# +# spec file for package vsss +# +# Copyright (c) 2020 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + +%define unmangled_version 0.230910-1 +Name: verysimpleslideshow +Version: 0.230910 +Release: 1 +Summary: CLI script for creating slideshows +License: Public Domain +URL: https://prymula.ct8.pl +Source0: %{name}-%{unmangled_version}.tar.gz +%if 0%{?suse_version}>=150000 +BuildRequires: python3 +Requires: python3, ffmpeg +%endif +%if 0%{?fedora}>=36 +BuildRequires: python3 +Requires: python3, ffmpeg +%endif +BuildArch: noarch + +%description + + +%prep +%setup -n %{name}-%{unmangled_version} -n %{name}-%{unmangled_version} +rm -f %{_bindir}/vsss + +%post +%{__ln_s} -f %{_bindir}/verysimpleslideshow.py %{_bindir}/vsss + +%postun +rm -f %{_bindir}/vsss + +%build + +%install + +mkdir -p %{buildroot}/usr +mkdir -p %{buildroot}/usr/bin +mkdir -p %{buildroot}/usr/share +mkdir -p %{buildroot}/usr/share/verysimpleslideshow + + +install -m 0755 verysimpleslideshow.py %{buildroot}/usr/bin +install -m 0644 CzytajTo %{buildroot}/usr/share/verysimpleslideshow + + + +%files +%defattr(0755,root,root) +/usr/bin/verysimpleslideshow.py +/usr/share/verysimpleslideshow/ +%defattr(0644,root,root) +/usr/share/verysimpleslideshow/CzytajTo + +%changelog + diff -r 000000000000 -r dcd610585610 verysimpleslideshow/run.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/run.txt Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,1 @@ +ffmpeg -f concat -r 1 -i list.txt -framerate 0.3 -c:v mpeg4 output.mp4 \ No newline at end of file diff -r 000000000000 -r dcd610585610 verysimpleslideshow/verysimpleslideshow.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verysimpleslideshow/verysimpleslideshow.py Thu Sep 21 22:32:14 2023 +0200 @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 +# Very Simple Slide Show +# autor: Prymula (PRP) +# licencja: Public Domain +# data 02-09-2023 +# edytor: Geany + +import os, sys +import tempfile +import shutil +from sys import platform +from PIL import Image +from itertools import filterfalse + +VER = "0.230913-0" + +if platform == "linux" or platform == "linux2": + SLA = '/' +elif platform == "darwin": + SLA = '/' +elif platform == "win32": + SLA = '\\' + +#count = 0 +tmp_path = tempfile.mkdtemp() +#mp_path = dir_path = os.getcwd() +#list_path = dir_path = os.getcwd() +tmp_list = tmp_path + SLA + "list.txt" +# NIE USUWAC ! +#command = "ffmpeg -framerate 0.3 -pattern_type glob -i '"+tmp_path+"/*.jpg' -c:v libopenh264 -profile:v high -crf 20 -pix_fmt yuv420p output.mp4" +command = "ffmpeg -f concat -y -r 0.2 -safe 0 -i '"+tmp_list+"' -framerate 1 -c:v libx264 -profile:v high -crf 20 -pix_fmt yuv420p -filter:a 'atempo=0.5' output.mp4" +#command = "ffmpeg -f concat -y -r 0.2 -safe 0 -i '"+tmp_list+"' -framerate 1 -c:v mpeg4 -crf 20 -pix_fmt yuv420p output.mp4" + +#num = 0 +sample = None +try: + sample = Image.Resampling.LANCZOS + print ("Uzywam LANCZOS") +except AttributeError: + sample = Image.ANTIALIAS + print ("Uzywam: ANTIALIAS") + + +class App(): + + BASE_APSC = 724 + LONG = 1080 + HALF = 540 + BASE_MICRO = 810 + BASE = None + #n_pictures = 0 + count = 0 + + def list_files(self, dir_path): + res = [] + try: + for file_path in os.listdir(dir_path): + if os.path.isfile(os.path.join(dir_path, file_path)): + res.append(os.path.join(dir_path, file_path)) + except FileNotFoundError: + print(f"The directory {dir_path} does not exist") + except PermissionError: + print(f"Permission denied to access the directory {dir_path}") + except OSError as e: + print(f"An OS error occurred: {e}") + return res + + def file_exclusion(self, files): + for f in files[:]: + if f.rfind('.jpg') == -1: + #print('usuwam: '+f) + files.remove(f) + return files + + def get_files(self): + dir_path = os.getcwd() + print ("GETCWD: "+dir_path) + files = self.list_files(dir_path) + return a.file_exclusion(files) + + def create_horizontal_surface(self): + return Image.new('RGB', (a.LONG, a.BASE)) + + def create_vertical_surface(self): + return Image.new('RGB', (a.BASE, a.LONG)) + + def put_horizontal_image(self, image): + global sample + new = Image.new("RGB", (a.LONG, a.BASE)) + if image.size[0] != a.LONG: + image = image.resize((a.LONG, a.BASE), sample, None, 3.0) + new.paste(image, (0,0), mask = image) + return new + + def put_vertical_image(self, image): + global sample + new = Image.new("RGB", (a.BASE, a.LONG)) + if image.size[0] != a.BASE: + image = image.resize((a.BASE, a.LONG), sample, None, 3.0) + new.paste(image, (0,0), mask = image) + return new + + # dodaje portrety do kraiobrazu + def join_image_to_horizontal(self, image, destinity): + global sample # n_pictures + single = False + print ("DLUGOSC: "+str(len(image))) + if len(image) == 1: # and n_pictures > 0: # 1 + print ("SINGIELEK") + #num += 1 # tymczasowy + tmp = image[-1]. resize((a.HALF, a.BASE), sample, None, 3.0) + destinity.paste(tmp, (300,0), mask = tmp) + image.pop() + single = True + elif len(image) > 0: + #num += 1 # tymczasowy + tmp = image[-1].resize((a.HALF, a.BASE), sample, None, 3.0) + destinity.paste(tmp, (0,0), mask = tmp) + image.pop() + if len(image) > 0: + #num += 1 # tymczasowy + tmp = image[-1]. resize((a.HALF, a.BASE), sample, None, 3.0) + destinity.paste(tmp, (a.HALF,0), mask = tmp) + image.pop() + + return destinity, len(image), single + + # dodaje kraiobrazy do portretu + def join_image_to_vertical(self, image, destinity): + global sample # n_pictures + single = False + print ("DLUGOSC: "+str(len(image))) + if len(image) == 1: # and a.n_pictures > 0: # 1 + print ("SINGIELEK") + #num += 1 # tymczasowy + tmp = image[-1]. resize((a.BASE, a.HALF), sample, None, 3.0) + destinity.paste(tmp, (0,300), mask = tmp) + image.pop() + single = True + #a.n_pictures -= 1 + elif len(image) > 0: + #num += 1 # tymczasowy + tmp = image[-1].resize((a.BASE, a.HALF), sample, None, 3.0) + destinity.paste(tmp, (0,0), mask = tmp) + image.pop() + #a.n_pictures -= 1 + if len(image) > 0: + #num += 1 # tymczasowy + tmp = image[-1]. resize((a.BASE, a.HALF), sample, None, 3.0) + destinity.paste(tmp, (0, a.HALF), mask = tmp) + image.pop() + #a.n_pictures -= 1 + + return destinity, len(image), single + + def read_images_from_directory(self, files): + #global n_pictures + images_vertical = [] + images_horizontal = [] + if len(files) == 0: + print ('Brak plików JPG !') + a.cleanup() + exit (1) + for f in files: + image = Image.open(f).convert('RGBA') + if image.size[1] > image.size[0]: + s = float(image.size[0] / image.size[1]) + if s > 0.6 and s < 0.69: + a.BASE = a.BASE_APSC + print ("obraz vertical APSC: "+f+" s:"+str(s)) + images_vertical.insert(-1, image) + #a.n_pictures += 1 + elif s > 0.7 and s < 0.79: + a.BASE = a.BASE_MICRO + print ("obraz vertical MICRO: "+f+" s:"+str(s)) + images_vertical.insert(-1, image) + #a.n_pictures += 1 + else: + print ("Ver - Niedozwolony rozmiar zdjęcia s: "+str(s) + " Foto: "+f) + + if image.size[0] > image.size[1]: + s = float(image.size[0] / image.size[1]) + if s > 1.4 and s <= 1.51: + a.BASE = a.BASE_APSC + print ("obraz horizontal APSC: "+f+" s:"+str(s)); + images_horizontal.insert(-1, image) + #a.n_pictures += 1 + elif s > 1.3 and s < 1.39: + a.BASE = a.BASE_MICRO + print ("obraz horizontal MICRO: "+f+" s:"+str(s)); + images_horizontal.insert(-1, image) + #a.n_pictures += 1 + else: + print ("Hor - Niedozwolony rozmiar zdjęcia s: "+str(s) + " Foto: "+f) + + + + #print ("N_PICTURES: "+str(a.n_pictures)) + return images_vertical, images_horizontal + + def cleanup(self): + # NIEBEZPIECZNA ! lepiej niczym innym nie nadpisywać tych zmiennych + shutil.rmtree(tmp_path) + + def save_vertical_to_horizontal(self, images_vertical, jpg_names): + #global count + for i in images_vertical[:]: + output, list_size, single = a.join_image_to_horizontal(images_vertical, a.create_horizontal_surface()) + if list_size != 0 or single == True: + #output.show() + output.save(str(tmp_path)+SLA+jpg_names[a.count], format='JPEG') + a.count += 1 + + def save_horizontal_to_vertical(self, images_horizontal, jpg_names): + #global count + for i in images_horizontal[:]: + output, list_size, single = a.join_image_to_vertical(images_horizontal, a.create_vertical_surface()) + if list_size != 0 or single == True: + #output.show() + output.save(str(tmp_path)+SLA+jpg_names[a.count], format='JPEG') + a.count += 1 + + def save_vertical(self, images_vertica, jpg_names): + #global count + for i in images_vertical[:]: + output = a.put_vertical_image(i) + output.save(str(tmp_path)+SLA+jpg_names[a.count], format='JPEG') + #output.show() + images_vertical.pop() + a.count += 1 + + def save_horizontal(self, images_horizontal, jpg_names): + #global count + for i in images_horizontal[:]: + output = a.put_horizontal_image(i) + output.save(str(tmp_path)+SLA+jpg_names[a.count], format='JPEG') + #output.show() + images_horizontal.pop() + a.count += 1 + + def name_jpg_wihout_path(self): + files_jpg_names = a.file_exclusion(os.listdir()) + for f in files_jpg_names: + f = f[0] + "file " + #print ("Ekstrat: " + str(files_jpg_names)) + return files_jpg_names + + def at_unslash(self, path): + p = path.replace("\\","/") + print("PO KONWERSJI: "+p) + return p + + def dump_jpg_list(self, files_jpg_names): + print("tmp_list") + with open(tmp_list, 'w') as fp: + for item in files_jpg_names: + # FIXME - to nic nie daje dalej wyrzuca "invalid argument" + if platform == "win32": + fp.write("file '"+a.at_unslash(str(tmp_path) + SLA +"%s'\r\n" % item)) + else: + # write each item on a new line + fp.write("file '"+str(tmp_path) + SLA +"%s'\r\n" % item) + + print("Zapisany plik do list.txt --> "+item) + return files_jpg_names +a = App() +jpg_names = a.dump_jpg_list(a.name_jpg_wihout_path()) +if len(sys.argv) == 1 or sys.argv[-1] == '-H': + images_vertical, images_horizontal = a.read_images_from_directory(a.get_files()) + a.save_horizontal(images_horizontal, jpg_names) + a.save_vertical_to_horizontal(images_vertical, jpg_names) + #print ("_---------------N_PIC: "+str(a.n_pictures)) + + print(command) + os.popen(command).read() +elif sys.argv[-1] == '-V': + images_vertical, images_horizontal = a.read_images_from_directory(a.get_files()) + a.save_vertical(images_vertical, jpg_names) + a.save_horizontal_to_vertical(images_horizontal, jpg_names) + #print ("_---------------N_PIC: "+str(a.n_pictures)) + print(command) + os.popen(command).read() +else: + print ("\n\nUżycie:\n\n Uruchom skrypt w katalogu z plikami JPG\n\n 'vsss -H' | 'vsss' dla video horyzontalnego\n 'vsss -V' dla video vertykalnego\n\n \ + 'vsss -h' aby wyświetlić pomoc i numer wersji.\n verysimpleslideshow (vsss) ver. "+VER+"\n SEPT-2023") + + +a.cleanup()