File chooser dialog
| Anke (encarsia) | Also available in: Deutsch
Contents
FileChooserDialog
The GtkFileChooserDialog is a subclass of GtkDialog (see diaogue article) and provides opening and saving of files and folders.
Glade
The dialog can be added from the "Toplevel" section of the widget sidebar. In addition to the file browser itself the widget has an intern GtkBox for additional widgets and a GtkButtonBox as "action area" for buttons.
First the Gtk.FileChooserAction mode must be defined (see Python GI API Reference): open or save file, choose or create a folder.
Action area and Responses
The response signal is emitted on widget interaction in the action area which also passes the response value. So for these widgets there is no need to activate the clicked signal of buttons.
By default the "action area" is generated beneath the file browser area.
If the FileChooserDialog is used without Glade (see below) the buttons are created in the headerbar. This seems to be standard procedure because Glade generated dialogs induce the warning
Gtk-WARNING **: Content added to the action area of a dialog using header bars
This message is not shown if buttons are not added to the intern action area.
If a headerbar with buttons is created in Glade the buttons cannot be assigned to a response value.
There may be several solutions to the problem:
XML file
After creating a headerbar with button(s) the Glade file is opened in a text editor and add line(s) to the <action-widgets>
element:
<object class="GtkFileChooserDialog" id="filechooser_dialog"> <property ... ></property> <property ... ></property> <!-- ... --> <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> <!-- ... --> </object>
This works but this procedure is surely not the intended way to deal with the problem because after altering the Glade file the edit is retracted.
add_action_widget function
The add_action_widget
adds activatable widgets to the action area and hold a response value. This includes widgets of the Gtk.Activatable class: Buttons, MenuItem, RecentChooserMenu, Switch and ToolItem.
The scheme for creating a button is
widget.add_action_widget(button,response)
The widget property "can-default" of the button must be activated:
button.set_property("can-default",True)
In the example the standard buttons "apply/cancel" are added to the file dialog:
button = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL) button.set_property("can-default",True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.CANCEL) button = Gtk.Button.new_from_stock(Gtk.STOCK_APPLY) button.set_property("can-default",True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.OK)
To apply file selection on doubleclick the file-activated is also required in addition to the response signal.
Preview widget
The dialogue can contain an optional preview widget. To use it activate "Preview Widget Active" and choose a free widget (p.e. a GtkImage). It may be necessary to create the preview widget in an empty container widget and pull it into a free area.
If the preview requires a refresh the update-preview signal is emitted.
FileFilter
Files can be filtered according to certain criteria by using FileFilter. There can be defined several (shell style glob) patterns or MIME-types for each filter.
In Glade filters can be found in the widget sidebar in the "Miscellaneous" group. A filter for a dialog can be selected in the general widget properties. This corresponds to the set_filter
function.
Python
Dialog without Glade
The FileChooserDialog is a complex but also easy to use graphic interface item. Realizing the dialog without Glade also avoids the headerbar problem discussed above. Creating a dialog follows the scheme
dialog = Gtk.FileChooserDialog("window title", parent_window, file_chooser_action, (button1,response1, button2,response2))
The dialog then can be directly run and processed:
response = dialog.run() if response == response1: ... elif response == response2: ... dialog.destroy()
FileFilter
There are two possibilities to apply a FileFilter:
No user choice. The applied filter is preset:
dialog.set_filter(filter)
Selection per dropdown menu. The user can choose between different defined filters:
dialog.add_filter(filter1) dialog.add_filter(filter2) ...
Listings
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>
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)
Comments
Comments powered by Disqus