Launch command in a subprocess and create a dialog window to monitor the
    output of the process.

    Parameters
    ----------
    command : list or str
        Subprocess command to execute.
    shell : bool, optional
        If :data:`shell` is ``False``, :data:`command` **must** be a
        :class:`list`.

        If :data:`shell` is ``True``, :data:`command` **must** be a
        :class:`str`.
    title : str, optional
        Title for dialog window and initial contents of main label.
    data_callback : func(dialog, command_view, fd, data), optional
        Callback function called when data is available for one of the file
        descriptors.

        The :data:`fd` callback parameter is 1 for ``stdout`` and 2 for
        ``stderr``.
    **kwargs
        Additional keyword arguments are interpreted as dialog widget property
        values and are applied to the dialog widget.

    Returns
    -------
    gtk.Dialog
        Dialog with a progress bar and an expandable text view to monitor the
        output of the specified :data:`command`.

        .. note::

            Subprocess is launched before returning dialog.

def get_run_command_dialog(command, shell=False, title='', data_callback=None,
                           parent=None, **kwargs):
    '''
    Launch command in a subprocess and create a dialog window to monitor the
    output of the process.

    Parameters
    ----------
    command : list or str
        Subprocess command to execute.
    shell : bool, optional
        If :data:`shell` is ``False``, :data:`command` **must** be a
        :class:`list`.

        If :data:`shell` is ``True``, :data:`command` **must** be a
        :class:`str`.
    title : str, optional
        Title for dialog window and initial contents of main label.
    data_callback : func(dialog, command_view, fd, data), optional
        Callback function called when data is available for one of the file
        descriptors.

        The :data:`fd` callback parameter is 1 for ``stdout`` and 2 for
        ``stderr``.
    **kwargs
        Additional keyword arguments are interpreted as dialog widget property
        values and are applied to the dialog widget.

    Returns
    -------
    gtk.Dialog
        Dialog with a progress bar and an expandable text view to monitor the
        output of the specified :data:`command`.

        .. note::

            Subprocess is launched before returning dialog.
    '''
    dialog = gtk.Dialog(title=title or None, parent=parent)
    dialog.set_size_request(540, -1)
    for key, value in kwargs.iteritems():
        setattr(dialog.props, key, value)

    dialog.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK)
    dialog.set_default_response(gtk.RESPONSE_OK)

    content_area = dialog.get_content_area()
    label = gtk.Label(title)
    label.props.xalign = .1
    content_area.pack_start(label, expand=False, fill=True, padding=10)

    progress_bar = gtk.ProgressBar()

    expander = gtk.Expander('Details')

    # Resize window based on whether or not expander is open.
    expander.connect('activate', functools
                     .partial(lambda w, e, *args:
                              w.set_size_request(540, -1 if e.props.expanded
                                                 else 480), dialog))

    command_view = CommandTextView()
    if data_callback is not None:
        command_view.connect('data-written', functools.partial(data_callback,
                                                               dialog))

    expander.add(command_view.widget)

    content_area.pack_start(progress_bar, expand=False)
    content_area.pack_start(expander, expand=True, fill=True)

    button = dialog.get_action_area().get_children()[0]
    content_area.show_all()

    def _run_command(label, progress_bar, button, view, command, shell):
        button.props.sensitive = False
        text_buffer = command_view.text_view.get_buffer()
        text_buffer.delete(*text_buffer.get_bounds())

        def _pulse(*args):
            progress_bar.pulse()
            return True

        timeout_id = gobject.timeout_add(250, _pulse)
        command_view.run(command, shell=shell)
        gobject.source_remove(timeout_id)
        progress_bar.set_fraction(1.)
        button.props.sensitive = True
        label.set_markup('{} <b>done</b>.'.format(title))

    gobject.idle_add(_run_command, label, progress_bar, button, command_view,
                     command, shell)
    return dialog