def _run(procedure_name, procedure_params):
   global config
   
   if config.PLUGIN_NAME == config._DEFAULT_PLUGIN_NAME:
     config.PLUGIN_NAME = procedure_name
     _init_config_builtin_delayed(config)
   
   procedure = _add_gui_excepthook(
     _procedures_names[procedure_name], procedure_params[0])
   
   if hasattr(gimpui, "gimp_ui_init"):
     gimpui.gimp_ui_init()
   
   procedure(*procedure_params)
Exemple #2
0
    def _run(procedure_name, procedure_params):
        global config

        if config.PLUGIN_NAME == config._DEFAULT_PLUGIN_NAME:
            config.PLUGIN_NAME = procedure_name
            _init_config_builtin_delayed(config)

        procedure = _add_gui_excepthook(_procedures_names[procedure_name],
                                        procedure_params[0])

        if hasattr(gimpui, "gimp_ui_init"):
            gimpui.gimp_ui_init()

        procedure(*procedure_params)
Exemple #3
0
def do_console():
    import pygtk
    pygtk.require('2.0')

    import sys, gobject, gtk, gimpenums, gimpshelf, gimpui, pyconsole
    gimpui.gimp_ui_init()

    namespace = {
        '__builtins__': __builtins__,
        '__name__': '__main__',
        '__doc__': None,
        'gimp': gimp,
        'pdb': gimp.pdb,
        'shelf': gimpshelf.shelf
    }

    for s in gimpenums.__dict__.keys():
        if s[0] != '_':
            namespace[s] = getattr(gimpenums, s)

    class GimpConsole(pyconsole.Console):
        def __init__(self, quit_func=None):
            banner = ('GIMP %s Python Console\nPython %s\n' %
                      (gimp.pdb.gimp_version(), sys.version))
            pyconsole.Console.__init__(self,
                                       locals=namespace,
                                       banner=banner,
                                       quit_func=quit_func)

        def _commit(self):
            pyconsole.Console._commit(self)
            gimp.displays_flush()

    class ConsoleDialog(gimpui.Dialog):
        def __init__(self):
            gimpui.Dialog.__init__(self,
                                   title=_("Python Console"),
                                   role=PROC_NAME,
                                   help_id=PROC_NAME,
                                   buttons=(gtk.STOCK_SAVE, RESPONSE_SAVE,
                                            gtk.STOCK_CLEAR, RESPONSE_CLEAR,
                                            _("_Browse..."), RESPONSE_BROWSE,
                                            gtk.STOCK_CLOSE,
                                            gtk.RESPONSE_CLOSE))

            self.set_alternative_button_order(
                (gtk.RESPONSE_CLOSE, RESPONSE_BROWSE, RESPONSE_CLEAR,
                 RESPONSE_SAVE))

            self.cons = GimpConsole(quit_func=lambda: gtk.main_quit())

            self.connect('response', self.response)

            self.browse_dlg = None
            self.save_dlg = None

            vbox = gtk.VBox(False, 12)
            vbox.set_border_width(12)
            self.vbox.pack_start(vbox)

            scrl_win = gtk.ScrolledWindow()
            scrl_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
            vbox.pack_start(scrl_win)

            scrl_win.add(self.cons)

            width, height = self.cons.get_default_size()
            sb_width, sb_height = scrl_win.get_vscrollbar().size_request()

            # Account for scrollbar width and border width to ensure
            # the text view gets a width of 80 characters. We don't care
            # so much whether the height will be exactly 40 characters.
            self.set_default_size(width + sb_width + 2 * 12, height)

        def response(self, dialog, response_id):
            if response_id == RESPONSE_BROWSE:
                self.browse()
            elif response_id == RESPONSE_CLEAR:
                self.cons.banner = None
                self.cons.clear()
            elif response_id == RESPONSE_SAVE:
                self.save_dialog()
            else:
                gtk.main_quit()

            self.cons.grab_focus()

        def browse_response(self, dlg, response_id):
            if response_id != gtk.RESPONSE_APPLY:
                dlg.hide()
                return

            proc_name = dlg.get_selected()

            if not proc_name:
                return

            proc = pdb[proc_name]

            cmd = ''

            if len(proc.return_vals) > 0:
                cmd = ', '.join(
                    [x[1].replace('-', '_') for x in proc.return_vals]) + ' = '

            cmd = cmd + 'pdb.%s' % proc.proc_name.replace('-', '_')

            if len(proc.params) > 0 and proc.params[0][1] == 'run-mode':
                params = proc.params[1:]
            else:
                params = proc.params

            cmd = cmd + '(%s)' % ', '.join(
                [x[1].replace('-', '_') for x in params])

            buffer = self.cons.buffer

            lines = buffer.get_line_count()
            iter = buffer.get_iter_at_line_offset(lines - 1, 4)
            buffer.delete(iter, buffer.get_end_iter())
            buffer.place_cursor(buffer.get_end_iter())
            buffer.insert_at_cursor(cmd)

        def browse(self):
            if not self.browse_dlg:
                dlg = gimpui.ProcBrowserDialog(
                    _("Python Procedure Browser"),
                    role=PROC_NAME,
                    buttons=(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
                             gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))

                dlg.set_default_response(gtk.RESPONSE_APPLY)
                dlg.set_alternative_button_order(
                    (gtk.RESPONSE_CLOSE, gtk.RESPONSE_APPLY))

                dlg.connect('response', self.browse_response)
                dlg.connect('row-activated',
                            lambda dlg: dlg.response(gtk.RESPONSE_APPLY))

                self.browse_dlg = dlg

            self.browse_dlg.present()

        def save_response(self, dlg, response_id):
            if response_id == gtk.RESPONSE_DELETE_EVENT:
                self.save_dlg = None
                return
            elif response_id == gtk.RESPONSE_OK:
                filename = dlg.get_filename()

                try:
                    logfile = open(filename, 'w')
                except IOError, e:
                    gimp.message(
                        _("Could not open '%s' for writing: %s") %
                        (filename, e.strerror))
                    return

                buffer = self.cons.buffer

                start = buffer.get_start_iter()
                end = buffer.get_end_iter()

                log = buffer.get_text(start, end, False)

                try:
                    logfile.write(log)
                    logfile.close()
                except IOError, e:
                    gimp.message(
                        _("Could not write to '%s': %s") %
                        (filename, e.strerror))
                    return

            dlg.hide()
Exemple #4
0
def _interact(proc_name, start_params):
    (blurb, help, author, copyright, date, label, imagetypes, plugin_type,
     params, results, function, menu, domain, on_query,
     on_run) = _registered_plugins_[proc_name]

    def run_script(run_params):
        params = start_params + tuple(run_params)
        _set_defaults(proc_name, params)
        return apply(function, params)

    params = params[len(start_params):]

    # short circuit for no parameters ...
    if len(params) == 0:
        return run_script([])

    import pygtk
    pygtk.require('2.0')

    import gimpui
    import gtk
    #    import pango
    gimpui.gimp_ui_init()

    defaults = _get_defaults(proc_name)
    defaults = defaults[len(start_params):]

    class EntryValueError(Exception):
        pass

    def warning_dialog(parent, primary, secondary=None):
        dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
                                gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE,
                                primary)
        if secondary:
            dlg.format_secondary_text(secondary)
        dlg.run()
        dlg.destroy()

    def error_dialog(parent, proc_name):
        import sys, traceback

        exc_str = exc_only_str = _("Missing exception information")

        try:
            etype, value, tb = sys.exc_info()
            exc_str = "".join(traceback.format_exception(etype, value, tb))
            exc_only_str = "".join(
                traceback.format_exception_only(etype, value))
        finally:
            etype = value = tb = None

        title = _("An error occurred running %s") % proc_name
        dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
                                gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, title)
        dlg.format_secondary_text(exc_only_str)

        alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
        alignment.set_padding(0, 0, 12, 12)
        dlg.vbox.pack_start(alignment)
        alignment.show()

        expander = gtk.Expander(_("_More Information"))
        expander.set_use_underline(True)
        expander.set_spacing(6)
        alignment.add(expander)
        expander.show()

        scrolled = gtk.ScrolledWindow()
        scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrolled.set_size_request(-1, 200)
        expander.add(scrolled)
        scrolled.show()

        label = gtk.Label(exc_str)
        label.set_alignment(0.0, 0.0)
        label.set_padding(6, 6)
        label.set_selectable(True)
        scrolled.add_with_viewport(label)
        label.show()

        def response(widget, id):
            widget.destroy()

        dlg.connect("response", response)
        dlg.set_resizable(True)
        dlg.show()

    # define a mapping of param types to edit objects ...
    class StringEntry(gtk.Entry):
        def __init__(self, default=""):
            gtk.Entry.__init__(self)
            self.set_text(str(default))
            self.set_activates_default(True)

        def get_value(self):
            return self.get_text()

    class TextEntry(gtk.ScrolledWindow):
        def __init__(self, default=""):
            gtk.ScrolledWindow.__init__(self)
            self.set_shadow_type(gtk.SHADOW_IN)

            self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
            self.set_size_request(100, -1)

            self.view = gtk.TextView()
            self.add(self.view)
            self.view.show()

            self.buffer = self.view.get_buffer()

            self.set_value(str(default))

        def set_value(self, text):
            self.buffer.set_text(text)

        def get_value(self):
            return self.buffer.get_text(self.buffer.get_start_iter(),
                                        self.buffer.get_end_iter())

    class IntEntry(StringEntry):
        def get_value(self):
            try:
                return int(self.get_text())
            except ValueError, e:
                raise EntryValueError, e.args
Exemple #5
0
def _interact(proc_name, start_params):
    (
        blurb,
        help,
        author,
        copyright,
        date,
        label,
        imagetypes,
        plugin_type,
        params,
        results,
        function,
        menu,
        domain,
        on_query,
        on_run,
    ) = _registered_plugins_[proc_name]

    def run_script(run_params):
        params = start_params + tuple(run_params)
        _set_defaults(proc_name, params)
        return apply(function, params)

    params = params[len(start_params) :]

    # short circuit for no parameters ...
    if len(params) == 0:
        return run_script([])

    import pygtk

    pygtk.require("2.0")

    import gimpui
    import gtk

    #    import pango
    gimpui.gimp_ui_init()

    defaults = _get_defaults(proc_name)
    defaults = defaults[len(start_params) :]

    class EntryValueError(Exception):
        pass

    def warning_dialog(parent, primary, secondary=None):
        dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, primary)
        if secondary:
            dlg.format_secondary_text(secondary)
        dlg.run()
        dlg.destroy()

    def error_dialog(parent, proc_name):
        import sys, traceback

        exc_str = exc_only_str = _("Missing exception information")

        try:
            etype, value, tb = sys.exc_info()
            exc_str = "".join(traceback.format_exception(etype, value, tb))
            exc_only_str = "".join(traceback.format_exception_only(etype, value))
        finally:
            etype = value = tb = None

        title = _("An error occurred running %s") % proc_name
        dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, title)
        dlg.format_secondary_text(exc_only_str)

        alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
        alignment.set_padding(0, 0, 12, 12)
        dlg.vbox.pack_start(alignment)
        alignment.show()

        expander = gtk.Expander(_("_More Information"))
        expander.set_use_underline(True)
        expander.set_spacing(6)
        alignment.add(expander)
        expander.show()

        scrolled = gtk.ScrolledWindow()
        scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrolled.set_size_request(-1, 200)
        expander.add(scrolled)
        scrolled.show()

        label = gtk.Label(exc_str)
        label.set_alignment(0.0, 0.0)
        label.set_padding(6, 6)
        label.set_selectable(True)
        scrolled.add_with_viewport(label)
        label.show()

        def response(widget, id):
            widget.destroy()

        dlg.connect("response", response)
        dlg.set_resizable(True)
        dlg.show()

    # define a mapping of param types to edit objects ...
    class StringEntry(gtk.Entry):
        def __init__(self, default=""):
            gtk.Entry.__init__(self)
            self.set_text(str(default))
            self.set_activates_default(True)

        def get_value(self):
            return self.get_text()

    class TextEntry(gtk.ScrolledWindow):
        def __init__(self, default=""):
            gtk.ScrolledWindow.__init__(self)
            self.set_shadow_type(gtk.SHADOW_IN)

            self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
            self.set_size_request(100, -1)

            self.view = gtk.TextView()
            self.add(self.view)
            self.view.show()

            self.buffer = self.view.get_buffer()

            self.set_value(str(default))

        def set_value(self, text):
            self.buffer.set_text(text)

        def get_value(self):
            return self.buffer.get_text(self.buffer.get_start_iter(), self.buffer.get_end_iter())

    class IntEntry(StringEntry):
        def get_value(self):
            try:
                return int(self.get_text())
            except ValueError, e:
                raise EntryValueError, e.args
def save_spr(img, drawable, filename, raw_filename):
    from array import array
    import pygtk
    import gtk
    pygtk.require('2.0')

    THUMB_MAXSIZE = 128
    RESPONSE_EXPORT = 1
    MIN_FRAME_ORIGIN = -8192
    MAX_FRAME_ORIGIN = 8192
    LS_LAYER, LS_PIXBUF, LS_SIZE_INFO, LS_EXPORT, LS_ORIGIN_X, LS_ORIGIN_Y, LS_THUMBDATA = range(
        7)

    spr_img = img.duplicate()
    gimpui.gimp_ui_init()

    if spr_img.base_type != INDEXED:
        try:
            pdb.gimp_convert_indexed(spr_img, NO_DITHER, MAKE_PALETTE, 256, 0,
                                     0, '')
        except RuntimeError:
            # Gimp does not support indexed mode if the image contains layer groups, so delete them
            for layer in spr_img.layers:
                if pdb.gimp_item_is_group(layer):
                    pdb.gimp_image_merge_layer_group(spr_img, layer)
            pdb.gimp_convert_indexed(spr_img, NO_DITHER, MAKE_PALETTE, 256, 0,
                                     0, '')

    def make_thumbnail_data(layer):
        width = layer.width
        height = layer.height

        indices = layer.get_pixel_rgn(0, 0, width, height)[:, :]
        if layer.type == INDEXEDA_IMAGE:
            indices = indices[::2]
        indices = ''.join(i + i for i in indices)

        thumbnail_layer = gimp.Layer(spr_img, layer.name + '_temp', width,
                                     height, INDEXEDA_IMAGE, 100, NORMAL_MODE)
        thumbnail_rgn = thumbnail_layer.get_pixel_rgn(0, 0, width, height)
        thumbnail_rgn[:, :] = indices
        pdb.gimp_image_insert_layer(spr_img, thumbnail_layer, None, 0)

        scale = float(THUMB_MAXSIZE) / max(width, height)
        if scale < 1.0:
            width = max(int(width * scale), 1)
            height = max(int(height * scale), 1)
            pdb.gimp_layer_scale_full(thumbnail_layer, width, height, False, 0)

        width, height, bpp, unused_, tn_data = pdb.gimp_drawable_thumbnail(
            thumbnail_layer, width, height)
        spr_img.remove_layer(thumbnail_layer)
        return str(bytearray(tn_data)), width, height, bpp

    def get_thumbnail(thumbnail_data, texture_format):
        tn_data, width, height, bpp = thumbnail_data

        last_index = len(spr_img.colormap) // 3 - 1

        if texture_format != Sprite.TEXTURE_FORMAT_INDEXALPHA:
            tn_data = array('B', tn_data)
            if texture_format == Sprite.TEXTURE_FORMAT_ADDITIVE:
                for i in xrange(0, len(tn_data), 4):
                    tn_data[i + 3] = sum(tn_data[i:i + 3]) // 3
            elif texture_format == Sprite.TEXTURE_FORMAT_ALPHATEST:
                for i in xrange(0, len(tn_data), 4):
                    tn_data[i +
                            3] = 0xff - (tn_data[i + 3] // last_index * 0xff)
            else:
                for i in xrange(0, len(tn_data), 4):
                    tn_data[i + 3] = 0xff
            tn_data = tn_data.tostring()

        return gtk.gdk.pixbuf_new_from_data(tn_data, gtk.gdk.COLORSPACE_RGB,
                                            True, 8, width, height,
                                            width * bpp)

    class ExportDialog(gimpui.Dialog):
        def __init__(self):
            gimpui.Dialog.__init__(
                self,
                title=ugettext('Export Image as Half-Life sprite'),
                role=EDITOR_PROC,
                help_id=None,
                buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE,
                         ugettext('Export'), RESPONSE_EXPORT))

            self.set_name(EDITOR_PROC)
            self.connect('response', self.on_response)
            self.connect('destroy', self.on_destroy)

            export_opt_box = self.make_export_options_box()
            self.img_view_frame = self.make_frames_view(
                reversed(spr_img.layers))

            hbox = gtk.HBox()
            hbox.pack_start(export_opt_box, True, True, 20)
            hbox.pack_start(self.img_view_frame, True, True, 5)

            self.vbox.pack_start(hbox)
            self.vbox.show_all()

            self.set_resizable(False)
            self.get_widget_for_response(RESPONSE_EXPORT).grab_focus()

        def update_thumbnails(self):
            texture_format = self.cb_tf.get_active()
            for ls in self.liststore:
                ls[LS_PIXBUF] = get_thumbnail(ls[LS_THUMBDATA], texture_format)

        def make_export_options_box(self):
            # Sprite type
            spr_type = spr_img.parasite_find('spr_type')
            self.cb_st = gtk.combo_box_new_text()
            self.cb_st.append_text('VP Parallel Upright')
            self.cb_st.append_text('Facing Upright')
            self.cb_st.append_text('VP Parallel')
            self.cb_st.append_text('Oriented')
            self.cb_st.append_text('VP Parallel Oriented')
            self.cb_st.set_tooltip_text(ugettext('Sprite Type'))
            self.cb_st.set_active(spr_type.flags if spr_type else 0)

            box = gtk.VBox(True, 5)
            box.pack_start(self.cb_st, False, False)

            st_frame = gimpui.Frame('Sprite Type:')
            st_frame.set_shadow_type(gtk.SHADOW_IN)
            st_frame.add(box)

            # Texture format
            texture_format = spr_img.parasite_find('spr_format')
            self.cb_tf = gtk.combo_box_new_text()
            self.cb_tf.append_text('Normal')
            self.cb_tf.append_text('Additive')
            self.cb_tf.append_text('Indexalpha')
            self.cb_tf.append_text('Alphatest')
            self.cb_tf.set_tooltip_text(ugettext('Texture Format'))
            self.cb_tf.set_active(
                texture_format.flags if texture_format else 0)

            def cb_tf_changed(cb):
                self.update_thumbnails()

            self.cb_tf.connect('changed', cb_tf_changed)

            box = gtk.VBox(True, 5)
            box.pack_start(self.cb_tf, False, False)

            tf_frame = gimpui.Frame('Texture Format:')
            tf_frame.set_shadow_type(gtk.SHADOW_IN)
            tf_frame.add(box)

            # Add origins offset
            lbl_oo_x = gtk.Label('Origin X:')

            adjustment = gtk.Adjustment(lower=MIN_FRAME_ORIGIN,
                                        upper=MAX_FRAME_ORIGIN,
                                        step_incr=1)
            self.sb_oo_x = gtk.SpinButton(adjustment=adjustment,
                                          climb_rate=1,
                                          digits=0)
            self.sb_oo_x.set_tooltip_text(ugettext('Offset for origin X'))

            box_origin_x = gtk.HBox(True, 12)
            box_origin_x.pack_start(lbl_oo_x, False, False)
            box_origin_x.pack_start(self.sb_oo_x, False, False)

            lbl_oo_y = gtk.Label('Origin Y:')

            adjustment = gtk.Adjustment(lower=MIN_FRAME_ORIGIN,
                                        upper=MAX_FRAME_ORIGIN,
                                        step_incr=1)
            self.sb_oo_y = gtk.SpinButton(adjustment=adjustment,
                                          climb_rate=1,
                                          digits=0)
            self.sb_oo_y.set_tooltip_text(ugettext('Offset for origin Y'))

            box_origin_y = gtk.HBox(True, 12)
            box_origin_y.pack_start(lbl_oo_y, False, False)
            box_origin_y.pack_start(self.sb_oo_y, False, False)

            def btn_oo_clicked(btn):
                offset_x = self.sb_oo_x.get_value_as_int()
                offset_y = self.sb_oo_y.get_value_as_int()
                for ls in self.liststore:
                    ls[LS_ORIGIN_X] += offset_x
                    ls[LS_ORIGIN_Y] += offset_y

            btn_oo = gtk.Button('Add offsets')
            btn_oo.connect('clicked', btn_oo_clicked)

            box = gtk.VBox(True, 5)
            box.pack_start(box_origin_x, False, False)
            box.pack_start(box_origin_y, False, False)
            box.pack_start(btn_oo, False, False)

            oo_frame = gimpui.Frame('Add origin offsets:')
            oo_frame.set_shadow_type(gtk.SHADOW_IN)
            oo_frame.add(box)

            # Main option frame
            o_box = gtk.VBox()
            o_box.set_size_request(110, -1)
            o_box.pack_start(st_frame, False, False, 10)
            o_box.pack_start(tf_frame, False, False, 10)
            o_box.pack_start(oo_frame, False, False, 10)

            box = gtk.VBox()
            box.set_size_request(140, -1)
            box.pack_start(o_box, True, False)

            return box

        def make_frames_view(self, layers):
            import gobject

            texture_format = self.cb_tf.get_active()
            self.liststore = gtk.ListStore(gobject.TYPE_PYOBJECT,
                                           gtk.gdk.Pixbuf, str,
                                           gobject.TYPE_BOOLEAN,
                                           gobject.TYPE_INT, gobject.TYPE_INT,
                                           gobject.TYPE_PYOBJECT)
            for l in layers:
                frames = [gl for gl in reversed(l.layers)
                          ] if pdb.gimp_item_is_group(l) else [l]
                for f in frames:
                    thumbnail_data = make_thumbnail_data(f)
                    pixbuf = get_thumbnail(thumbnail_data, texture_format)
                    size_info = '<b>Size</b>: %d x %d' % (f.width, f.height)
                    parasite_origins = f.parasite_find('spr_origins')
                    if parasite_origins:
                        origin_x, origin_y = unpack('<2i',
                                                    parasite_origins.data[:8])
                    else:
                        origin_x, origin_y = -f.width // 2, f.height // 2
                    self.liststore.append([
                        f, pixbuf, size_info, True, origin_x, origin_y,
                        thumbnail_data
                    ])

            self.export_frames_num = len(self.liststore)
            self.iconview = gtk.TreeView(self.liststore)
            self.iconview.set_reorderable(True)

            self.iconview.set_enable_search(False)
            self.iconview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)

            # Column 'Export'
            def on_cb_export_toggled(widget, path):
                export = not self.liststore[path][LS_EXPORT]
                self.liststore[path][LS_EXPORT] = export
                self.export_frames_num += 1 if export else -1
                self.set_btn_export_sensitive(self.export_frames_num > 0)
                self.img_view_frame.set_label('Frames to export: %d' %
                                              self.export_frames_num)

            cb_export = gtk.CellRendererToggle()
            cb_export.connect('toggled', on_cb_export_toggled)

            col_export = gtk.TreeViewColumn('Export',
                                            cb_export,
                                            active=LS_EXPORT)
            col_export_header = gtk.Label('Export')
            col_export_header.show()

            tt_export = gtk.Tooltips()
            tt_export.set_tip(col_export_header, 'Export frame to file.')

            col_export.set_sort_order(gtk.SORT_DESCENDING)
            col_export.set_sort_column_id(4)

            col_export.set_widget(col_export_header)
            self.iconview.append_column(col_export)

            # Column 'Frame'
            pixrend = gtk.CellRendererPixbuf()
            col_pixbuf = gtk.TreeViewColumn('Frame', pixrend, pixbuf=LS_PIXBUF)
            col_pixbuf.set_min_width(THUMB_MAXSIZE)
            self.iconview.append_column(col_pixbuf)

            # Column 'Settings'
            col_info = gtk.TreeViewColumn()
            col_info_header = gtk.Label('Settings')
            col_info_header.show()
            col_info.set_widget(col_info_header)

            tt_info = gtk.Tooltips()
            tt_info.set_tip(col_info_header, 'Frame export options.')

            # Info text
            renderer = gtk.CellRendererText()
            renderer.set_property('yalign', 0.3)
            renderer.set_property('xalign', 0.0)
            renderer.set_property('width', 0)
            renderer.set_property('height', THUMB_MAXSIZE)
            col_info.pack_start(renderer, False)
            col_info.set_attributes(renderer, markup=LS_SIZE_INFO)

            # Label origin X
            adjustment = gtk.Adjustment(lower=MIN_FRAME_ORIGIN,
                                        upper=MAX_FRAME_ORIGIN,
                                        step_incr=1)
            renderer = gtk.CellRendererText()
            renderer.set_property('markup', '<b>Origin X</b>:')
            renderer.set_property('width', 64)
            col_info.pack_start(renderer, False)

            # crs origin x
            adjustment = gtk.Adjustment(lower=MIN_FRAME_ORIGIN,
                                        upper=MAX_FRAME_ORIGIN,
                                        step_incr=1)
            renderer = gtk.CellRendererSpin()
            renderer.set_property('editable', True)
            renderer.set_property('adjustment', adjustment)

            def on_crs_origin_x_changed(widget, path, val):
                val = min(max(int(val), MIN_FRAME_ORIGIN), MAX_FRAME_ORIGIN)
                self.liststore[path][LS_ORIGIN_X] = val

            renderer.connect('edited', on_crs_origin_x_changed)

            col_info.pack_start(renderer)
            col_info.set_attributes(renderer, markup=LS_ORIGIN_X)

            # Label origin y
            renderer = gtk.CellRendererText()
            renderer.set_property('markup', '<b>Origin Y</b>:')
            renderer.set_property('width', 64)
            col_info.pack_start(renderer, False)

            # crs origin y
            renderer = gtk.CellRendererSpin()
            renderer.set_property('editable', True)
            renderer.set_property('adjustment', adjustment)

            def on_crs_origin_x_changed(widget, path, val):
                val = min(max(int(val), MIN_FRAME_ORIGIN), MAX_FRAME_ORIGIN)
                self.liststore[path][LS_ORIGIN_Y] = val

            renderer.connect('edited', on_crs_origin_x_changed)

            col_info.pack_start(renderer)
            col_info.set_attributes(renderer, markup=LS_ORIGIN_Y)

            self.iconview.append_column(col_info)

            scrl_win = gtk.ScrolledWindow()
            scrl_win.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
            scrl_win.add(self.iconview)
            scrl_win.set_size_request(THUMB_MAXSIZE, THUMB_MAXSIZE * 4)

            frame_imgs = gimpui.Frame('Frames to export: %d' %
                                      self.export_frames_num)
            frame_imgs.set_property('label-xalign', 0.05)
            frame_imgs.set_shadow_type(gtk.SHADOW_IN)
            frame_imgs.add(scrl_win)
            frame_imgs.set_size_request(535, -1)

            return frame_imgs

        def export_selected_frames(self):
            layers = []
            for row in self.liststore:
                if not row[LS_EXPORT]:
                    continue

                layer = row[LS_LAYER]
                origins_data = pack('<2i', row[LS_ORIGIN_X], row[LS_ORIGIN_Y])
                if layer.parasite_find('spr_origins'):
                    layer.parasite_detach('spr_origins')
                layer.attach_new_parasite('spr_origins', 0, origins_data)

                layers.append(layer)

            # Make grouped layers with parasites
            grouped_layers, added_layers = [], []
            for layer in layers:
                if layer in added_layers:
                    continue

                added_layers.append(layer)

                parent = layer.parent
                if not parent:
                    grouped_layers.append([layer])
                    continue

                if not parent.parasite_find('spr_type'):
                    parent.attach_new_parasite('spr_type', 1, '')

                group_lst = [ll for ll in layers if ll.parent == parent]
                for i, ll in enumerate(group_lst):
                    interval = ll.parasite_find('spr_interval')
                    if not interval:
                        ll.attach_new_parasite('spr_interval', 0,
                                               pack('<f', (i + 1) * 0.1))

                    added_layers.append(ll)
                grouped_layers.append([parent] + group_lst)

            # Export to file
            if grouped_layers:
                Sprite.save_to_file(spr_img, filename, grouped_layers,
                                    self.cb_st.get_active(),
                                    self.cb_tf.get_active())

        def set_btn_export_sensitive(self, sensitive):
            self.get_widget_for_response(RESPONSE_EXPORT).set_sensitive(
                sensitive)

        def on_response(self, dialog, response_id):
            self.destroy()
            while gtk.events_pending():
                gtk.main_iteration()

            if response_id == RESPONSE_EXPORT:
                self.export_selected_frames()

        def on_destroy(self, widget):
            gtk.main_quit()

        def run(self):
            self.show()
            gtk.main()

    ExportDialog().run()
    pdb.gimp_image_delete(spr_img)