Dateiauswahldialog
| Anke (encarsia) | Auch verfügbar in: English
Inhalt
FileChooserDialog
Der Gtk.FileChooserDialog ist eine Subclass von Gtk.Dialog (siehe Artikel zu Dialogen) und ermöglicht das Auswählen und Speichern von Dateien oder Ordnern.
Glade
Den Dialog findet man in der Widget-Seitenleiste oben unter "Oberste Ebene". Neben dem Dateibrowser besitzt er eine erweiterbare interne Gtk.Box für weitere Widgets sowie eine Gtk.ButtonBox als interne "action area" für Buttons.
Es ist erforderlich anzugeben, für welche Aktion der Dialog gedacht ist, was Gtk.FileChooserAction entspricht (siehe Python GI API Reference): Datei öffnen oder speichern, Ordner auswählen oder anlegen.
Action area und Responses
Responses sind Antwortkennungen, die beim Auslösen des Signals response übergeben werden. Buttons in der "action area" werden jeweils Response-Werte zugewiesen anstatt das clicked-Signal der Buttons zu nutzen (weitere Erklärungen dazu im Artikel zu Dialogen).
Standardmäßig wird die "action area" unter dem Dateibrowserbereich angelegt.
Verwendet man den FileChooserDialog ohne Glade (siehe unten), werden die Buttons in der Headerbar angezeigt. Letzteres sollte aber vermutlich der Standard sein, da es eine Warnung ausgegeben wird, die die Funktionalität des Dialogs allerdings nicht beeinträchtigt:
Gtk-WARNING **: Content added to the action area of a dialog using header bars
Diese Meldung wird nicht angezeigt, wenn man darauf verzichtet, in Glade Buttons zur intern action area hinzuzufügen. Dies betrifft auch andere Dialogarten.
Legt man nun in Glade eine Headerbar mit Buttons an, ist es standardmäßig nicht möglich, diesen Buttons Response-Werte zuzuweisen.
Dafür gibt es (mindestens) zwei Lösungsmöglichkeiten:
XML-Datei
Man legt die Headerbar mit Button(s) an, anschließend öffnet man die Glade-Datei in einem Texteditor und fügt dem Element <action-widgets>
die entsprechenden Zeilen hinzu:
<object class="GtkFileChooserDialog" id="filechooser_dialog"> <property abc ></property> <property xyz ></property> <!-- usw. --> <action-widgets> <!-- Buttons innerhalb der action area --> <action-widget response="0">button1</action-widget> <action-widget response="1">button2</action-widget> <!-- Button in Headerbar --> <action-widget response="-1">hb_button</action-widget> </action-widgets> <!-- usw. --> </object>
Dies funktioniert zwar, ist aber ganz sicher nicht so gedacht, weil diese Änderung beim erneuten Bearbeiten der Glade-Datei wieder rückgängig gemacht wird.
add_action_widget-Funktion
Mit der Funktion add_action_widget
können aktivierbare Widgets zur action area hinzugefügt und damit ebenfalls per response-Signal verarbeitet werden. Dies sind Widgets der Gtk.Activatable-Klasse und beinhaltet die Widgets Buttons, MenuItem, RecentChooserMenu, Switch und ToolItem.
Ein Button wird nach dem Schema
widget.add_action_widget(button, response)
hinzugefügt. Wichtig ist es, beim Button die Widget-Eigenschaft "can-default" zu aktivieren:
button.set_property("can-default", True)
Im Beispiel erhält der Dialog die beiden Standardbuttons "OK"/"Cancel":
button = Gtk.Button.new_with_label("Cancel") button.set_property("can-default", True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.CANCEL) button = Gtk.Button.new_with_label("OK") button.set_property("can-default", True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.OK)
Um die Dateiauswahl auch auf Doppelklick zu ermöglichen, wird neben des response-Signals noch das Signal file-activated benötigt.
Vorschau-Widget
Der Dialog besitzt die Option, ein Vorschau-Widget einzubinden. Dafür aktiviert man in den Dialog-Eigenschaften "Vorschau-Widget aktiv" und wählt unter "Vorschau-Widget" ein freies Widget (z.B. ein GtkImage). Möglicherweise muss man dieses Widget zunächst in ein leeres Container-Widget erstellen und dort in einen freien Bereich ziehen.
Wenn eine Aktualisierung der Vorschau angefordert wird, wird das Signal update-preview ausgelöst.
FileFilter
FileFilter dienen dazu, Dateien bestimmten Musters anzuzeigen. Pro Filter können mehrere (shell style glob) Patterns oder MIME-Types angegeben werden.
Den Filter findet man in Glade unter "Sonstiges". Im Dialog kann man in den allgemeinen Widget-Einstellungen den gewünschten Filter auswählen. Dies entspricht der set_filter
-Funktion.
Python
Dialog ohne Glade
Der FileChooserDialog lässt sich auch ziemlich einfach ohne Glade realisieren, zudem lassen sich die oben genannten Probleme mit Buttons in der Headerbar vermeiden. Der Dialog wird nach folgendem Schema erstellt:
dialog = Gtk.FileChooserDialog(title="window title", parent=parent_window, action=file_chooser_action) dialog.add_buttons(button1, response1, button2, response2)
Der Dialog wird dann direkt aufgerufen und verarbeitet:
response = dialog.run() if response == response1: ... elif response == response2: ... dialog.destroy()
FileFilter
Es gibt zwei Möglichkeiten, einen Filefilter anzuwenden:
Ohne Wahl. Der anzuwendende Filter ist voreingestellt:
dialog.set_filter(filter)
Wahl per Dropdown-Menü: Der Nutzer kann zwischen mehreren vorgegebenen Filtern wählen:
dialog.add_filter(filter1) dialog.add_filter(filter2) ...
Listings
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import os import sys import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gio, GdkPixbuf class Handler: def on_window_destroy(self, window): window.close() def on_dialog_close(self, widget, *event): widget.hide_on_delete() return True def on_filechooser_dialog_response(self, widget, response): if response == -6: print("Cancel") elif response == -5: print("File selection: {}".format(widget.get_filename())) self.on_dialog_close(widget) def on_filechooser_dialog_file_activated(self, widget): self.on_filechooser_dialog_response(widget, -5) def on_filechooser_dialog_update_preview(self, widget): if widget.get_filename() != None and os.path.isfile(widget.get_filename()): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(widget.get_filename(), 200, 200, True, ) app.obj("preview").set_from_pixbuf(pixbuf) def on_file_button_clicked(self,widget): app.obj("filechooser_dialog").show_all() def on_dir_button_clicked(self,widget): dialog = Gtk.FileChooserDialog(title="Choose a folder", parent=app.obj("window"), action=Gtk.FileChooserAction.SELECT_FOLDER, ) dialog.set_default_size(600, 300) dialog.add_buttons("Cancel", Gtk.ResponseType.CANCEL, "OK", Gtk.ResponseType.OK) response = dialog.run() if response == Gtk.ResponseType.OK: print("Folder selection: {}".format(dialog.get_filename())) elif response == Gtk.ResponseType.CANCEL: print("Cancel") dialog.destroy() class ExampleApp: def __init__(self): self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0)) self.app.connect("activate", self.on_app_activate) self.app.connect("shutdown", self.on_app_shutdown) def on_app_activate(self, app): builder = Gtk.Builder() builder.add_from_file("16_filechooser.glade") builder.connect_signals(Handler()) self.obj = builder.get_object self.obj("window").set_application(app) self.obj("window").show_all() #add filters to filechooser dialog self.obj("filefilter").set_name("Image files") self.obj("filechooser_dialog").add_filter(self.obj("filefilter")) self.obj("png_filter").set_name("PNG files") self.obj("filechooser_dialog").add_filter(self.obj("png_filter")) self.obj("jpg_filter").set_name("JPG files") self.obj("filechooser_dialog").add_filter(self.obj("jpg_filter")) #add buttons to headerbar of Glade generated dialog button = Gtk.Button.new_with_label("Cancel") button.set_property("can-default", True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.CANCEL) button = Gtk.Button.new_with_label("OK") button.set_property("can-default", True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.OK) def on_app_shutdown(self, app): self.app.quit() def run(self, argv): self.app.run(argv) app = ExampleApp() app.run(sys.argv)
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkFileFilter" id="filefilter"> <mime-types> <mime-type>image/*</mime-type> </mime-types> </object> <object class="GtkFileFilter" id="jpg_filter"> <mime-types> <mime-type>image/jpeg</mime-type> </mime-types> </object> <object class="GtkFileFilter" id="png_filter"> <mime-types> <mime-type>image/png</mime-type> </mime-types> </object> <object class="GtkImage" id="preview"> <property name="width_request">200</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="margin_right">5</property> </object> <object class="GtkApplicationWindow" id="window"> <property name="width_request">300</property> <property name="height_request">200</property> <property name="can_focus">False</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="homogeneous">True</property> <child> <object class="GtkButton" id="file_button"> <property name="label" translatable="yes">Choose an image file...</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_file_button_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="dir_button"> <property name="label" translatable="yes">Choose folder...</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_dir_button_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> </child> <child type="titlebar"> <placeholder/> </child> </object> <object class="GtkFileChooserDialog" id="filechooser_dialog"> <property name="width_request">800</property> <property name="height_request">500</property> <property name="can_focus">False</property> <property name="type_hint">dialog</property> <property name="transient_for">window</property> <property name="attached_to">window</property> <property name="preview_widget">preview</property> <property name="use_preview_label">False</property> <signal name="delete-event" handler="on_dialog_close" swapped="no"/> <signal name="file-activated" handler="on_filechooser_dialog_file_activated" swapped="no"/> <signal name="response" handler="on_filechooser_dialog_response" swapped="no"/> <signal name="update-preview" handler="on_filechooser_dialog_update_preview" swapped="no"/> <child internal-child="vbox"> <object class="GtkBox" id="fcbox"> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child internal-child="action_area"> <object class="GtkButtonBox"> <property name="can_focus">False</property> <child> <object class="GtkButton" id="button2"> <property name="label">gtk-cancel</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> <child> <object class="GtkButton" id="button1"> <property name="label">gtk-apply</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">3</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">0</property> </packing> </child> </object> </child> <action-widgets> <action-widget response="-6">button2</action-widget> <action-widget response="-5">button1</action-widget> </action-widgets> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title">Choose image...</property> <property name="show_close_button">True</property> </object> </child> <action-widgets> <action-widget response="-6">button2</action-widget> <action-widget response="-5">button1</action-widget> </action-widgets> </object> </interface>
Kommentare
Comments powered by Disqus