File Choosers and Printing
The file chooser is the dialog for the open and save interactions: the gtk.FileChooserDialog (or the FileChooserWidget embedded in a custom dialog), with its action mode (open, save, select-folder), its filters (the file-type restrictions), and its state (the current folder, the selected file). The printing is the gtk.PrintOperation - the app describes the pages, the operation handles the dialog and the device, and the render callback draws each page with Cairo. This chapter covers the chooser's construction (the action, the buttons, the filter), the open and save flows, the filter definition, the current-folder and selection state, and the print operation's structure (the begin/page/end callbacks, the Cairo render).
The FileChooserDialog: Open
import gtk
# the open dialog - the action OPEN, the standard buttons:
def choose_file_to_open():
dialog = gtk.FileChooserDialog(
"Open File", # the title
None, # the parent (None: top)
gtk.FILE_CHOOSER_ACTION_OPEN, # the action mode
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
# the button tuples: (stock_id, response_id)
# the filter: the file types the chooser shows
filter = gtk.FileFilter()
filter.set_name("Text files")
filter.add_pattern("*.txt")
filter.add_pattern("*.md")
dialog.add_filter(filter)
# the "all files" filter (the usual last entry):
allf = gtk.FileFilter()
allf.set_name("All files")
allf.add_pattern("*")
dialog.add_filter(allf)
# the current folder (the starting point):
dialog.set_current_folder("/home/user/documents")
# the run/destroy (the modal pattern, the dialogs chapter):
filename = None
if dialog.run() == gtk.RESPONSE_OK:
filename = dialog.get_filename()
dialog.destroy()
return filename
# the get_filename is the selected path (None if cancelled)
# the chooser is the standard open flow: the filter narrows
# the view, the current folder is the start, the run blocks
# until the choice, the filename is the result
The FileChooserDialog is the open dialog, constructed with the title, the parent, the action mode (FILE_CHOOSER_ACTION_OPEN, _SAVE, _SELECT_FOLDER), and the button tuples (the stock id plus the response id for each). The filter is the file-type restriction - the FileFilter with a name and the add_pattern globs (*.txt, *.md), added to the dialog, and the "all files" filter as the usual last entry - the filter is what narrows the chooser's view to the types the app opens. The set_current_folder is the starting point (the last-used folder, the documents directory), and the run/get_filename/destroy is the modal pattern the dialogs chapter established: the run blocks until the response, the get_filename is the selected path (the None on the cancel), and the destroy follows. The chooser is the standard open flow, and the same construction with the SAVE action (plus the set_current_name for the suggested filename) is the save flow.
The Save Flow and the State
import gtk
def choose_file_to_save(suggested="untitled.txt"):
dialog = gtk.FileChooserDialog(
"Save File", None,
gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK))
# the save-specific setup:
dialog.set_do_overwrite_confirmation(True)
# the overwrite prompt: if the file exists, the chooser
# asks "replace?" before returning OK - the built-in guard
dialog.set_current_name(suggested)
# the suggested filename (pre-filled in the name entry)
dialog.add_filter(text_filter())
path = None
if dialog.run() == gtk.RESPONSE_OK:
path = dialog.get_filename()
dialog.destroy()
return path
# the selection state (the embedded FileChooserWidget form):
# the get_filenames (the SELECT_FOLDER / multi mode), the
# get_current_folder, the select_filename (the programmatic
# selection - the "jump to this file" after a search)
chooser = gtk.FileChooserWidget(gtk.FILE_CHOOSER_ACTION_OPEN)
chooser.set_current_folder("/tmp")
chooser.select_filename("/tmp/target.txt")
print(chooser.get_filename())
# the local-only flag: restrict to the local filesystem
# (the network shares, the remote locations, excluded)
chooser.set_local_only(True)
The save flow is the same construction with the SAVE action and the two save-specific settings: the set_do_overwrite_confirmation(True) (the built-in "replace the existing file?" prompt, the guard against the silent overwrite) and the set_current_name(suggested) (the pre-filled filename in the chooser's name entry, the app's suggestion the user edits). The selection state is the chooser's API beyond the run: the get_current_folder (the folder the chooser is showing), the select_filename (the programmatic selection - the "jump to this file" the search result triggers), the get_filenames (the multi-select and the folder mode), and the set_local_only (the restriction to the local filesystem, excluding the network shares). The embedded FileChooserWidget is the same component without the dialog frame - packed into a custom dialog or a window, the run is replaced by the response signal (the non-blocking pattern), and the state API is identical. The chooser is the file interaction, and the state API is what makes it embeddable and programmable beyond the simple run.
The PrintOperation: The Structure
import gtk
import pango
# the PrintOperation - the app describes the pages, the
# operation handles the dialog, the device, and the render:
op = gtk.PrintOperation()
op.set_n_pages(3) # the page count (the setup)
# the begin: the job started (the context is the print context)
def on_begin(op, context):
# the page setup: the paper, the orientation
ps = context.get_page_setup()
# the context is where the job's state lives
return None
# the render: draw ONE page with Cairo (the page_num is 0-based)
def on_draw_page(op, context, page_num):
cr = context.cairo_create() # the page's Cairo context
w = context.get_width()
h = context.get_height()
# the margins: the printable area
# (the context's logic is the paper minus the margins)
cr.set_source_rgb(0, 0, 0)
cr.select_font_face("Sans", 0, pango.WEIGHT_NORMAL)
cr.set_font_size(24)
cr.move_to(50, 100)
cr.show_text("Page %d of the document" % (page_num + 1))
# the page's content: the document's page_num rendered
return None
# the end: the job finished
def on_end(op, context):
return None
op.connect("begin", on_begin)
op.connect("draw-page", on_draw_page)
op.connect("end", on_end)
# the run: the PRINT dialog (the destination, the copies),
# then the job on the selected device:
status, err = op.run(gtk.PRINT_ACTION_PRINT_DIALOG, None)
# status: PRINT_STATUS_OK on success, the error on failure
The PrintOperation is the printing structure: the app describes the job (the set_n_pages), connects the three callbacks (begin for the job start, the draw-page for each page's render, the end for the finish), and the run with the PRINT_ACTION_PRINT_DIALOG shows the print dialog (the destination, the copies) and then runs the job on the selected device. The draw-page callback is the render - the context.cairo_create() gives the page's Cairo context, the get_width/get_height the page dimensions, and the callback draws the page's content (the document's page_num page) with the same Cairo vocabulary the drawing chapter used. The context is the job's state (the page setup, the paper, the margins), and the begin/end bracket the job. The run's return is the status (PRINT_STATUS_OK on success, the error otherwise), which the app reports. The structure is the one the document printing uses: the pages described, the render per page, the dialog and the device handled by the operation - the app's content plus the toolkit's print plumbing.
The Working Rules
- The chooser is the
run/get_filename/destroypattern. The modal open and save are the dialogs chapter's pattern on the chooser - therunblocks, theget_filenameis the result (theNoneon cancel), thedestroyfollows. - Always add the "all files" filter last. The type filters narrow the view, and the
*filter is the escape for the file that does not match - the chooser without it hides the files the user knows are there. - The save flow sets the overwrite confirmation and the suggested name. The
set_do_overwrite_confirmation(True)is the silent-overwrite guard, and theset_current_nameis the app's suggestion the user edits - the two that make the save flow safe and useful. - The
FileChooserWidgetis the embeddable form. The same component without the dialog frame, theresponsesignal instead of therun(the non-blocking), the same state API - the form for the custom dialog and the in-window browser. - The print is the three callbacks plus the
run. Thebegin/draw-page/endare the job's structure, thedraw-pageis the per-page Cairo render, and therun(PRINT_ACTION_PRINT_DIALOG)is the dialog plus the job - the app's content, the toolkit's plumbing.
Summary
The file chooser is the open and save dialog: the FileChooserDialog with the action mode (the OPEN, the SAVE, the SELECT_FOLDER), the button tuples, the FileFilters (the type patterns plus the "all files" escape), the set_current_folder start, and the run/get_filename/destroy modal pattern. The save flow adds the set_do_overwrite_confirmation (the replace prompt) and the set_current_name (the suggested filename), and the state API (select_filename, the get_current_folder, the set_local_only) makes the chooser programmable and embeddable - the FileChooserWidget is the same component without the frame, the response signal instead of the run. The printing is the PrintOperation: the set_n_pages, the begin/draw-page/end callbacks (the draw-page is the per-page Cairo render on the context's surface), and the run with the PRINT_ACTION_PRINT_DIALOG that shows the dialog and runs the job. The two together - the chooser for the file, the operation for the print - are the file-interaction and the output the application UI needs, both built on the toolkit's standard components.