Tkinter-Fenster in Gimp

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
gahleitner
User
Beiträge: 35
Registriert: Montag 1. Mai 2017, 09:57

Samstag 19. August 2017, 09:57

Hallo!

Ich möchte mit dem Aufruf in GIMP ein GUI-Fenster öffnen, das ich mit einer Listbox, Checkboxen erweitern möchte. Hier mein Code:

Code: Alles auswählen

#!/usr/bin/env python

from gimpfu import *
from Tkinter import *
 
def newGUI():
    root=tkinter.Tk()
    root.geometry("250x140")
    root.title("Macrorecorder")
    mainloop()


register(
    "",#python_fu_test_hello_world",
    "",#Hello world",
    "",#Display a 'hello world' message",
    "JFM",
    "",#Open source (BSD 3-clause license)",
    "2013",
    "<Image>/File/Macrorecorder",
    "",
    [],
    [],
    newGUI)
    
main()
Ich bekomme immer in root=tkinter.Tk() eine Fehlermeldung - entweder tkinter ist nicht definiert oder es hat kein Modul Tk, je nach Schreibweise(gross,klein). Muss ich das bei Gimp anders definieren?
BlackJack

Samstag 19. August 2017, 12:11

@gahleitner: Also beim gezeigten Code ist ganz offensichtlich der Name `tkinter` nicht definiert. (Stimmt nicht: siehe weiter unten.)

Und Du solltest dringend mit den Sternchenimporten aufhören. Wenn bei den cirka 170 Namen die Du damit aus dem `Tkinter`-Modul in das Modul holst, ein Namen dabei ist der auch in `gimpfu` definiert ist, und den Du brauchst, dann hast Du ein Problem. Oder warst Du Dir jetzt 100% sicher das `Tkinter` nicht zufällig auch etwas mit dem Namen `register` definiert hat?

Hast Du alle 170 Namen im Kopf? Ich nicht, denn mein erster Satz in diesem Beitrag ist falsch! In `Tkinter` ist tatsächlich der Name `tkinter` definiert und daran ist das C-Erweiterungsmodul gebunden das Tcl/Tk einbettet, und *das* hat dann aber kein Attribut `Tk`.
__deets__
User
Beiträge: 4023
Registriert: Mittwoch 14. Oktober 2015, 14:29

Samstag 19. August 2017, 13:29

Ich gehe noch weiter und sage: geht nicht. Einfach mal so eben zwei GUI-Toolkits (GTK, abgekuerzt fuer "GIMP ToolKit") und Tkinter kann man ohne Hauptschleifenintegration nicht parallel betreiben.

Du solltest dich auf die von gimpfu bereitgestellten UI-Elemente konzentrieren.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Samstag 19. August 2017, 14:46

Denke ebenfalls, dass das praktisch nicht wirklich etwas bringen würde und so einfach nicht funktionieren dürfte. Mehr habe ich dazu nicht gefunden und eine Fehlermeldung, wie die im ersten Post notierte, erhalte ich halt auch:

http://gimpchat.com/viewtopic.php?f=8&t=14883

Und zu was auch?
Alles was man schreiben möchte, lässt sich als Python-Fu-Plugin realisieren. Gimp bietet genügend Widgets, die sich bei Bedarf definieren lassen und diese werden durch die zu übergebenen Parameter definiert.
gahleitner
User
Beiträge: 35
Registriert: Montag 1. Mai 2017, 09:57

Sonntag 20. August 2017, 15:46

Hallo!
Vielen Dank für die Anregung. Ich dachte, da tkinter auch in Gimp integriert ist, sollte es auch wie sonst funktionieren. Aber welche Widgets gibt es in Gimp, mit denen ich etwa das gleiche (Fenster, Listbox mit Scrollbar,...) erreiche? Im Prozedurenbrowser konnte ich nur Definitionen für Funktionen in Gimp finden.

Tschüss
Gerhard
__deets__
User
Beiträge: 4023
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 20. August 2017, 16:18

Woher nimmst du denn, dass tkinter integriert ist in GIMP? Du meinst du kannst es importieren?

Was die Widgets angeht: https://www.gimp.org/docs/python/
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Sonntag 20. August 2017, 19:02

Zusätzliche Widgets lassen sich mit PyGTK erzeugen, wozu Du das "PyGTK 2.0 Tutorial" durcharbeiten könntest.

http://www.pygtk.org/dist/pygtk2-tut.pdf

Dabei kommst Du aber nicht ohne die Gimp Prozeduren aus, denn es nutzt Dir kein neuer Button etwas, wenn damit nichts angesprochen wird. Die Beispiele in der PDF enthalten Links zu den Scripts und diese lassen sich so anpassen, dass die innerhalb von Gimp laufen. Zumindest hat das bei so einem "Hello World" Beispiel geklappt, wobei ich print durch gimp.message() ersetzte und

Code: Alles auswählen

if __name__ == "__main__":
    hello = HelloWorld()
    hello.main()
durch:

Code: Alles auswählen

def pygtk_test():
    hello = HelloWorld()
    hello.main()

register(
    "pygtk_test",
    "Nur ein Test",
    "Nur ein Test",
    "Mele Melewo",
    "MIT-Lizenz",
    "2017",
    "Test pygtk",
    None,
    [],
    [],
    pygtk_test,
    menu="<Image>/Eigene/Test")

main()
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Montag 21. August 2017, 15:31

Habe ein weiteres Beispiel aus dem Tutorial angepasst und getestet. Was ich noch nicht verstehe, warum die in den ersten beiden Beispielen die "main" noch mit in der Klasse definierten und in den nachfolgenden Beispielen als eigenständige Funktion.

http://www.pygtk.org/pygtk2tutorial/sec ... ample.html

Wenn die außerhalb der Klasse mit "main" definiert wird, hängt sich Gimp beim Starten und Registrieren der Plugins bereits auf, weil die dann wohl mit der unteren main kollidiert, so dass die zwangsläufig umbenannt werden muss. Nun gut, ich verstehe noch nicht viel davon und finde es ohnehin wichtiger, sich erst einmal richtig mit den Möglichkeiten von Plugins mit gimpfu zu beschäftigen.

Das Widget entspricht vom Aufbau der Abbildung, die auf der oben verlinkten Seite zu sehen ist und der jeweilige Text wird in der Fußzeile ausgegeben.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Angepasstes Beispiel von "www.pygtk.org/pygtk2tutorial/sec-TablePackingExample.html"

from gimpfu import register, main
import gimp
import pygtk            # Diese beiden Zeilen sollten eigentlich nur erforderlich
pygtk.require("2.0")    # sein, falls mehr als eine Version vorhanden sein sollte.
import gtk

class Table:

    def __init__(self):
        # Ein neues Widget erstellen
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Table")
        self.window.connect("delete_event", self.delete_event)
        # Abstand des Inhalts vom Außenrand
        self.window.set_border_width(40)
        
        # Tabelle 2x2 erstellen und ins Widget einfügen
        table = gtk.Table(2, 2, True)
        self.window.add(table)
  
        # Die Buttons mit Angaben zur Position
        button_topeins = gtk.Button("Button 01")
        button_topeins.connect("clicked", self.callback, "Button 01")
        table.attach(button_topeins, 0, 1, 0, 1)

        button_topzwei = gtk.Button("Button 02")
        button_topzwei.connect("clicked", self.callback, "Button 02")
        table.attach(button_topzwei, 1, 2, 0, 1)

        button_beenden = gtk.Button("Beenden")
        button_beenden.connect("clicked", lambda w: gtk.main_quit())
        table.attach(button_beenden, 0, 2, 1, 2)       
        
        # Alles anzeigen
        button_topeins.show()
        button_topzwei.show()
        button_beenden.show()
        table.show()
        self.window.show()
        
    # Ausgabe erfolgt in der Fußzeile
    def callback(self, widget, data=None):
        gimp.message("Hallo, {0} wurde gedrückt!".format(data))
       
    # Beenden
    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

# Die "main" darf hier nicht "main" heißen oder Gimp bleibt schon beim Starten hängen.
def hauptschleife():
    gtk.main()
    return 0       

def pygtk_dritter_test():
    Table()
    hauptschleife()

register(
    "pygtk_dritter_test",
    "Nur ein Test",
    "Nur ein dritter Test",
    "Mele Melewo",
    "MIT-Lizenz",
    "2017",
    "Tabelle mit Buttons",
    None,
    [],
    [],
    pygtk_dritter_test,
    menu="<Image>/Eigene/PyGTK-Tests")

main()
gahleitner
User
Beiträge: 35
Registriert: Montag 1. Mai 2017, 09:57

Dienstag 22. August 2017, 15:52

Hallo!

Hat mir sehr weitergeholfen. Vielen Dank.Aber wie kann ich einen Slider in eine Tabelle bekommen? Ich habe folgenden Code für den Slider:

Code: Alles auswählen

        
        label1=gtk.Label("Radius")
        radius=gtk.adjustment(5,0 ,0,0 ,100,0 ,1,0 ,1,0 ,1,0 ) #wert,unterer,oberer,schritt,seitensteigerung,seitengröße
        radius.connect("Radius",self.callback,label1)
        scaleo=gtk.HScale(radius)
        scaleo.set_digits(1)
#        hscale.set_draw_value (scharf.get_active())
        table.attach(label1, 2, 3, 4, 5)#Wert horiz. von 2-3, senkrecht von 4-5 - Tabellenlinien
        scaleo.show()
        label1.show()
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Dienstag 22. August 2017, 18:20

Das wird zwar in Kapitel 8 unter Range Widgets beschrieben, doch bisher sehe ich nicht, wozu das nützlich sein könnte. An was und wem möchtest Du die Werte übergeben?

Also es ist mir bislang zu hoch, da gehört schon etwas mehr an Einarbeitung zu, als bei mir zum jetzigen Zeitpunkt vorhanden und ich finde es in etwa so verständlich, wie einen Macrorecorder basteln zu wollen, ohne zu wissen, wo man mit dem einhaken könnte.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Mittwoch 23. August 2017, 15:16

Zu Deinem Slider usw., dafür brauchen keine neuen Widgets erstellt zu werden, sondern einfach einmal "www.gimp.org/docs/python/" und noch ein paar weitere Seiten durcharbeiteten sollte genügen, um mit Gimp das leichter und zuverlässiger zu erreichen.
Die Seite von gimp.org wurde ja bisher nicht nur einmal verlinkt und viele andere Seiten zusätzlich und wenn Du Dich damit in kleinen Schritten beschäftigen würdest, so würdest Du auch wissen, was Gimp alles zur Verfügung stellt, wenn die entsprechenden Parameter und Werte richtig übergeben werden. Und eine Seite wie die erwähnte durchzuarbeiten wird letztendlich mehr bringen, als nach Codefragmenten zu scharren, die dann nicht zusammen passen.

Für dieses Beispiel benutze ich noch einmal das letzte Beispiel aus diesem Thread und erweitere es lediglich um einige Parameter:

viewtopic.php?f=4&t=41064

Füge ich die Werte nicht die Funktion ein, sondern übergebe diese, wird Gimp ein Widget öffnen und das wird so aussehen wie ich es möchte, je nachdem ob ich PF_SLIDER oder PF_SPINNER wähle oder noch weitere Werte übergeben möchte. Und mit allen anderen Werten in anderen Plugins lässt es sich ähnlich handhaben.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from gimpfu import pdb, register, main, PF_IMAGE, PF_DRAWABLE, PF_SLIDER

def regle_schaerfe(image, ebene, radius, staerke, schwelle):
    pdb.gimp_image_undo_group_start(image)

    temp_image = pdb.plug_in_decompose(image, ebene, "HSV", 0)
    arbeits_ebene = pdb.gimp_image_get_active_layer(temp_image[0])
    pdb.plug_in_unsharp_mask(image, arbeits_ebene, radius, staerke, schwelle)
    neues_image = pdb.plug_in_compose(
                         temp_image[0],
                         arbeits_ebene,
                         temp_image[1],
                         temp_image[2],
                         temp_image[3],
                         "HSV")

    pdb.gimp_image_undo_group_end(image)
    pdb.gimp_display_new(neues_image)

    for i in range(len(temp_image)):
        if temp_image[i] is not None:
            pdb.gimp_image_delete(temp_image[i])

register(
    "regle_schaerfe",
    "Test Schärferegler",
    "Nur ein Test mit einem Schärferegler",
    "Mele Melewo",
    "MIT-Lizenz",
    "2017",
    "Schärferegler",
    "*",
    # PF_SLIDER - Voreinstellung, (min, max, Schrittweite)
    [
        (PF_IMAGE, "image", "Input Image", None),
        (PF_DRAWABLE, "ebene", "Input Ebene", None),
        (PF_SLIDER, "radius", "Radius", 0.5, (0.1, 100, 0.1)),
        (PF_SLIDER, "staerke", "Stärke", 5.0, (0.1, 10, 0.1)),
        (PF_SLIDER, "schwelle", "Schwellwert:", 0, (0, 255, 1))
    ],
    [],
    regle_schaerfe,
    menu="<Image>/Filters/Eigene")

main()
Rufe ich nun das Plugin auf, wird Gimp zuerst ein neues Widget öffnen und ich kann mir überlegen, ob ich die Vorgabewerte nur bestätigen oder eventuell noch ändern möchte.

Bild
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Freitag 25. August 2017, 20:52

gahleitner hat geschrieben:

Code: Alles auswählen

root.title("Macrorecorder")
Weil Du in den letzten Monaten bereits öfters etwas von einem "Macrorecorder" geschrieben hattest, also es gibt nicht funktionierende Ansätze, sowohl in der Roadmap unter Future - "Script recording and playback", als auch, wenn Du den Link - Bug 51937 - folgst, als Plugin, wobei das "the test script for the above" bereits im Jahre 2000 entwickelt wurde. Script und Kommentare zum "Bug 51937", verfasst von 2001 bis 2017.

Aus den Kommentaren geht die ganze Problematik hervor, warum sich ein Macro Recorder bisher nicht wirklich umsetzen ließ und die Kommentare gehören meiner Meinung nach zur Pflichtlektüre für jeden, der mit dem Gedanken spielen sollte, einen Macro Recorder entwickeln zu wollen.

Mein erster Gedanke war vor Tagen, bei "undo" einzuhaken und fand keinen Haken. Nun lese ich, es werden unter "Bearbeiten/Rückgängig" nicht wirklich die verwendeten Prozeduren aufgeführt, sondern nur die unter "Journal" angelegten internen Kopien eines Images. Werden drei Bearbeitungsschritte rückgängig gemacht, wird nur drei Versionen zurückgesprungen. Womit "undo" schon einmal kein Ansatz ist, um mit einen Macro Recorder einzuhaken, siehe Kommentar 30.
The undo journal does contain different versions of the image. It does not record how they have been created.
Der Rest war nicht weniger Interessant. Somit weiterhin mindestens bis zur übernächsten Version einen Zettel und einen Stift als manuellen Recorder benutzen und an Hand der Notizen dann Makros erstellen.
gahleitner
User
Beiträge: 35
Registriert: Montag 1. Mai 2017, 09:57

Samstag 26. August 2017, 14:20

Hallo!
Ich habe nun einen Checkbutton (für die Schärfe) und drei Slider (wie bei unscharf maskieren) erstellt.

Code: Alles auswählen

scharf = gtk.CheckButton("Schärfen")
        scharf.connect("toggled", self.check, "Schärfen")
#        scharf.connect("toggled", self.scale)
        scharf.set_active(True)
        table.attach(scharf, 2, 3, 3, 4)
        scharf.show()

       label1=gtk.Label("Radius")
        table.attach(label1,2,3,4,5)
        label1.show()
        adj1=gtk.Adjustment(4.0 ,0.0 ,500.0 ,0.1 ,0.1 ,0.0 ) #wert,unterer,oberer,schritt,seitensteigerung,seitengröße
        adj1.connect("value_changed",self.scale,"Radius")
        scale1=gtk.HScale(adj1)
        scale1.set_digits(1)#Nachkommastellen
##        scale1.set_draw_value (scharf.get_active())
#        scale_set_default_values(self.scale1)
        table.attach(scale1, 2, 3, 4, 5)#Wert horiz. von 2-3, senkrecht von 4-5 - Tabellenlinien
        scale1.show()

    def scale(self, widget,scharf):
        if scharf.set_active==True:
            self.scale(scharf.get_active())
            gimp.message(self)
Nun weiß ich nicht so recht, wie ich die Funktion "scale" schreiben soll. Es soll, wenn der Checkbutton "Schärfe" aktiv ist, die Werte der drei Slider übergeben. Darüber habe ich in der Anleitung für GTK nichts gefunden.
Noch eine Frage: Ich habe in GTK auch den FileChooserDialog und FileSelection gefunden - was ist besser, um mehrere Fotos aus einem Ordner in eine Listbox (ScrolledWindow) zu importieren, sortieren und dann der Reihe nach abzuarbeiten?
Kann mir jemand weiterhelfen.

Tschüss
Gerhard
gahleitner
User
Beiträge: 35
Registriert: Montag 1. Mai 2017, 09:57

Samstag 26. August 2017, 16:01

Hallo, Molewo!
Hab deinen Entwurf mit dem Slider erst jetzt gesehen - finde ich aber gut. Da ich mehrere Bearbeitungen brauche, habe ich damit nicht wirklich was angefangt. Hab deswegen schon mit GTK was gemacht - wie oben.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Samstag 26. August 2017, 17:11

Wenn Du nicht weiter weißt, was Du nun mit einem Widget im Fenster einer Anwendung anfangen solltest, ich weiß es auch nicht. Falls es so sein sollte, dann dürfte es wohl das Beste sein, Du begibst Dich noch einmal zu den Anfängen und beginnst noch einmal von vorne mit der Ein- und Durcharbeitung, denn wie man etwas checkt, nun ja, doch bereits bei den Anfängen hatte ich eine Seite verlinkt, auf der ein Zip-Archiv mit 12 Testdateien verlinkt war, darunter "test-batch-cartoon.py" und "test-batch-invert.py". Beide sind darauf ausgelegt, gleich ein Verzeichnis zu durchlaufen und checken dabei, ob es sich um JPG oder PNG Images handelt.

Alles was Du noch tun müsstest, sind ein paar kleinere Änderungen und Anpassungen, also z.B. schauen, ob die eine oder andere Prozedur veraltet ist und ob es dafür eine neue gibt. Dann Deine eigenen gewünschten Prozeduren anwenden bzw. die vorhandenen mit den von Dir gewünschten ersetzen. Bei der Registrierung die Reihenfolge des Beispiels in der gimpfu.py beachten und bei der Überarbeitung Dich vorzugweise nach der PEP 8 - Style Guide for Python Code richten, so z.B. bei der Neubenennung der Variablen und Dich vom Sternchenimport verabschieden. So würde ich es an Deiner Stelle dann nicht unbedingt übernehmen, dennoch sind die Beispiele alltagstauglich, denke ich mir.

Code: Alles auswählen

            # Build the full file paths.
            inputPath = inputFolder + "\\" + file
            outputPath = outputFolder + "\\" + file
Dann noch bei der String-Verknüpfung das in den letzten Monaten erworbene Python-Wissen anwenden, wie es besser gemacht wird und schon bist Du entweder fertig oder zumindest sehr viel weiter und könntest dann mit Beispielcode aufwarten, mit dem man etwas mehr anfangen kann.
Antworten