Exemple #1
0
class GalleryViewer:
    def __init__(self, title, parent, files, callback,
                       columns = 4,
                       thumb_size = 256,
                       width = 600,
                       height = 600):
        self.callback = callback
        self.thumb_size = thumb_size

        self.window = gtk.Window()
        self.window.set_position(gtk.WIN_POS_CENTER)
        self.window.set_resizable(False)
        self.window.set_modal(True)
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
        self.window.set_transient_for(parent)
        
        self.window.connect("key_press_event", self.on_key_press_event)

        # Main HBox of the window
        hbox = gtk.HBox(False, 5)
        self.window.add(hbox)

        # Right pane (location, iconview)
        vbox = gtk.VBox(False, 5)
        hbox.pack_start(vbox, True, True, 0)

        # Iconview
        self.iconview = gtk.IconView()
        self.iconview.set_pixbuf_column(0)
        self.iconview.set_markup_column(1)
        self.iconview.set_tooltip_column(2)
        self.iconview.set_selection_mode(gtk.SELECTION_SINGLE)
        self.iconview.set_item_width(self.thumb_size)
        self.iconview.set_columns(columns)

        self.iconview.connect("selection-changed", self.on_selection_changed)

        scrolled = gtk.ScrolledWindow()
        scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrolled.add_with_viewport(self.iconview)
        scrolled.set_size_request(int((thumb_size * 1.06) * columns), height)

        vbox.pack_start(scrolled, True, True, 0)

        # Data initialization:
        self.loader = Worker()
        self.loader.start()
        self.files = files
        self.items = []
        
    def run(self):
        self.window.show_all()
        self.update_model()
    
    def update_model(self):
        dialog = ProgressBarDialog(self.window, "Loading...")
        dialog.show()

        builder = ViewerListStoreBuilder(self.files, self.thumb_size)
        updater = Updater(builder.build(),
                          dialog.update,
                          self.on_model_ready,
                          (builder, dialog))
        updater.start()

    def on_model_ready(self, builder, dialog):
        dialog.destroy()
        self.loader.clear()

        for index, item in enumerate(builder.items):
            # Schedule an update on this item:
            self.loader.push((self.update_item_thumbnail, 
                             (builder.liststore, index, item)))

        # Update the items list:
        self.items = builder.items
        # Associate the new liststore to the iconview:
        self.iconview.set_model(builder.liststore)

    # This is done in a separate thread:
    def update_item_thumbnail(self, liststore, index, item):
        data = item.final_data()
        return (self.update_store_entry, (liststore, index, data))

    # This is requested to be done by the main thread:
    def update_store_entry(self, liststore, index, data):
        iter_ = liststore.get_iter((index,))
        liststore.set_value(iter_, 0, data[0])
        liststore.set_value(iter_, 1, data[1])
        liststore.set_value(iter_, 2, data[2])

    def on_key_press_event(self, widget, event, data=None):
        key_name = gtk.gdk.keyval_name(event.keyval)

        bindings = {
            "Escape" : self.close,
        }

        if key_name in bindings:
            bindings[key_name]()
            return True

    def on_selection_changed(self, iconview):
        selected = iconview.get_selected_items()

        if not selected:
            return

        index = selected[0][0]
        item = self.items[index]
        iconview.unselect_all()

        item.on_selected(self)
        
    def on_image_selected(self, item):
        self.callback(item.get_filename())
        self.close()

    def close(self):
        self.loader.stop()
        self.loader.join()
        gobject.idle_add(lambda window: window.destroy(), self.window)
Exemple #2
0
class GallerySelector:
    def __init__(self, title, parent, dirname, last_targets, on_file_selected, on_dir_selected,
                       dir_selector = False,
                       columns = 3,
                       thumb_size = 256,
                       quick_thumb_size = 32,
                       width = 600,
                       height = 600,
                       quick_width = 300):
        self.on_file_selected_cb = on_file_selected
        self.on_dir_selected_cb = on_dir_selected

        self.dir_selector = dir_selector
        self.thumb_size = thumb_size

        self.window = gtk.Window()
        self.window.set_position(gtk.WIN_POS_CENTER)
        self.window.set_resizable(False)
        self.window.set_modal(True)
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
        self.window.set_transient_for(parent)
        
        self.window.connect("key_press_event", self.on_key_press_event)

        # Main HBox of the window
        hbox = gtk.HBox(False, 5)
        self.window.add(hbox)

        # Left pane (quick access)
        vbox = gtk.VBox(False, 5)
        hbox.pack_start(vbox, True, True, 0)

        store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)

        store.append((GTKIconImage(gtk.STOCK_HARDDISK, quick_thumb_size).get_pixbuf(), 
                      "File System", "/"))

        home = os.path.realpath(os.path.expanduser("~"))
        store.append((GTKIconImage(gtk.STOCK_HOME, quick_thumb_size).get_pixbuf(), 
                      "Home", home))

        for home_dir in ["Downloads", "Documents", "Pictures", 
                         "Videos", "Dropbox", "Torrents"]:
            path = os.path.join(home, home_dir)
            if not os.path.isdir(path):
                continue
            thumb = DirectoryThumbnail(path) 
            store.append((thumb.get_pixbuf_at_size(quick_thumb_size, 
                                                   quick_thumb_size), 
                          home_dir, path))

        for directory in last_targets:
            thumb = DirectoryThumbnail(directory) 
            store.append((thumb.get_pixbuf_at_size(quick_thumb_size, 
                                                   quick_thumb_size), 
                          os.path.basename(directory),
                          directory))

        treeview = gtk.TreeView(store)
        treeview.set_headers_visible(False)
        treeview.connect_after("cursor-changed", self.on_cursor_changed)

        renderer = gtk.CellRendererPixbuf()
        column = gtk.TreeViewColumn("Icon", renderer, pixbuf=0)
        treeview.append_column(column)

        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn("Path", renderer, text=1)
        treeview.append_column(column)

        scrolled = gtk.ScrolledWindow()
        scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrolled.add_with_viewport(treeview)
        scrolled.set_size_request(quick_width, height)

        vbox.pack_start(scrolled, True, True, 0)

        # Right pane (location, iconview)
        vbox = gtk.VBox(False, 5)
        hbox.pack_start(vbox, True, True, 0)

        # Toolbar
        toolbar = gtk.Toolbar()
        toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)

        vbox.pack_start(toolbar, False, False, 0)

        button = gtk.ToolButton(gtk.STOCK_GO_UP)
        button.set_label("Go up")
        button.connect("clicked", self.on_go_up)
        button.set_is_important(True)
        toolbar.insert(button, -1)
        self.go_up = button

        button = gtk.ToolButton(gtk.STOCK_ADD)
        button.set_label("New folder")
        button.connect("clicked", self.on_new_folder)
        button.set_is_important(True)
        toolbar.insert(button, -1)

        button = gtk.ToolButton(gtk.STOCK_OPEN)
        button.set_label("Open Nautilus")
        button.connect("clicked", self.on_open_nautilus)
        button.set_is_important(True)
        toolbar.insert(button, -1)

        button = gtk.ToolButton(gtk.STOCK_REFRESH)
        button.connect("clicked", self.on_refresh)
        button.set_is_important(True)
        toolbar.insert(button, -1)

        # "Location"/"Filter" bar
        location_bar = gtk.HBox(False, 5)
        vbox.pack_start(location_bar, False, False, 0)

        label = gtk.Label()
        label.set_text("Location:")
        location_bar.pack_start(label, False, False, 0)

        self.location_entry = gtk.Entry()
        self.location_entry.connect("activate", self.on_location_entry_activate)
        location_bar.pack_start(self.location_entry, True, True, 0)

        self.filter_entry = gtk.Entry()
        self.filter_entry.connect("activate", self.on_filter_entry_activate)
        location_bar.pack_end(self.filter_entry, False, False, 0)

        label = gtk.Label()
        label.set_text("Filter:")
        location_bar.pack_end(label, False, False, 0)

        # Iconview
        self.iconview = gtk.IconView()
        self.iconview.set_pixbuf_column(0)
        self.iconview.set_markup_column(1)
        self.iconview.set_tooltip_column(2)
        self.iconview.set_selection_mode(gtk.SELECTION_SINGLE)
        self.iconview.set_item_width(self.thumb_size)
        self.iconview.set_columns(columns)

        self.iconview.connect("selection-changed", self.on_selection_changed)

        scrolled = gtk.ScrolledWindow()
        scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrolled.add_with_viewport(self.iconview)
        scrolled.set_size_request(int((thumb_size * 1.06) * columns), height)

        vbox.pack_start(scrolled, True, True, 0)

        # Buttonbar
        buttonbar = gtk.HBox(False, 0)

        self.info_label = gtk.Label()
        buttonbar.pack_start(self.info_label, False, False, 5)

        button = gtk.Button(stock=gtk.STOCK_OK)
        button.set_relief(gtk.RELIEF_NONE)
        button.connect("clicked", self.on_ok_clicked)
        buttonbar.pack_end(button, False, False, 5)

        button = gtk.Button(stock=gtk.STOCK_CANCEL)
        button.set_relief(gtk.RELIEF_NONE)
        button.connect("clicked", self.on_cancel_clicked)
        buttonbar.pack_end(button, False, False, 0)

        self.recursive_check = gtk.CheckButton(label="Recursive")
        self.recursive_check.set_active(False)
        self.recursive_check.set_sensitive(not dir_selector)
        buttonbar.pack_end(self.recursive_check, False, False, 0)

        vbox.pack_start(buttonbar, False, False, 5)

        # Enable icons in buttons:
        settings = gtk.settings_get_default()
        settings.props.gtk_button_images = True

        # Data initialization:
        self.loader = Worker()
        self.loader.start()

        self.curdir = os.path.realpath(os.path.expanduser(dirname))
        self.last_filter = ""
        self.items = []
        
    def run(self):
        self.window.show_all()
        self.filter_entry.grab_focus()
        self.update_model()
    
    def update_model(self, filter_=""):
        dialog = ProgressBarDialog(self.window, "Loading...")
        dialog.show()

        builder = SelectorListStoreBuilder(self.curdir, filter_, self.thumb_size)
        updater = Updater(builder.build(),
                          dialog.update,
                          self.on_model_ready,
                          (builder, dialog))
        updater.start()

    def on_model_ready(self, builder, dialog):
        dialog.destroy()
        self.loader.clear()

        for index, item in enumerate(builder.items):
            # Schedule an update on this item:
            self.loader.push((self.update_item_thumbnail, 
                             (builder.liststore, index, item)))

        # Update the items list:
        self.items = builder.items
        # Associate the new liststore to the iconview:
        self.iconview.set_model(builder.liststore)
        # Update the curdir entry widget:
        self.location_entry.set_text(self.curdir)
        # Update directory information:
        self.info_label.set_text("%d dirs, %d files" % builder.items_count)

    # This is done in a separate thread:
    def update_item_thumbnail(self, liststore, index, item):
        data = item.final_data()
        return (self.update_store_entry, (liststore, index, data))

    # This is requested to be done by the main thread:
    def update_store_entry(self, liststore, index, data):
        iter_ = liststore.get_iter((index,))
        liststore.set_value(iter_, 0, data[0])
        liststore.set_value(iter_, 1, data[1])
        liststore.set_value(iter_, 2, data[2])

    def on_key_press_event(self, widget, event, data=None):
        key_name = gtk.gdk.keyval_name(event.keyval)
        #print "gallery - key pressed:", key_name

        bindings = {
            "Escape" : self.close,
            "Up" : lambda: self.on_go_up(None),
        }

        if key_name in bindings:
            bindings[key_name]()
            return True

    def on_cursor_changed(self, treeview):
        selection = treeview.get_selection()
        model, iter_ = selection.get_selected()

        if iter_:
            directory = model.get_value(iter_, 2)
            self.on_dir_selected(directory)

    def on_go_up(self, widget):
        self.on_dir_selected(os.path.split(self.curdir)[0])

    def on_go_home(self, widget):
        self.on_dir_selected(os.path.realpath(os.path.expanduser('~')))

    def on_new_folder(self, widget):
        def on_entry(folder):
            manager = FileManager()
            manager.create_directory(os.path.join(self.curdir, folder))
            self.update_model()

        dialog = NewFolderDialog(self.window, on_entry)
        dialog.run()

    def on_open_nautilus(self, widget):
        execute(["nautilus", self.curdir])

    def on_refresh(self, widget):
        FileManager().on_dir_changed(self.curdir)
        self.update_model()

    def on_location_entry_activate(self, entry):
        directory = entry.get_text()
        if os.path.isdir(directory):
            self.on_dir_selected(directory)
        else:
            entry.set_text(self.curdir)

    def on_filter_entry_activate(self, entry):
        if (not entry.get_text() and 
            not self.last_filter):
            self.on_dir_selected_cb(self.curdir, self.recursive_check.get_active())
            self.close()
            return

        # Restrict the entries to those containing the filter:
        self.update_model(entry.get_text())
        self.last_filter = entry.get_text()

        # If only one item matches, simulate it's been selected:
        if len(self.items) == 1:
            self.items[0].on_selected(self)

    def on_selection_changed(self, iconview):
        selected = iconview.get_selected_items()

        if not selected:
            return

        index = selected[0][0]
        item = self.items[index]
        iconview.unselect_all()

        item.on_selected(self)
        
    def on_image_selected(self, item):
        if self.dir_selector:
            return
        self.on_file_selected_cb(item.get_filename())
        self.close()

    def on_dir_selected(self, item):
        scanner = FileScanner()
        dirs = scanner.get_dirs_from_dir(item)

        if self.dir_selector and not dirs:
            self.on_dir_selected_cb(item, self.recursive_check.get_active())
            self.close()
            return

        self.curdir = item
        self.go_up.set_sensitive(self.curdir != "/")
        self.update_model()
        self.last_filter = ""
        self.filter_entry.set_text("")
        self.filter_entry.grab_focus()

    def on_ok_clicked(self, button):
        self.on_dir_selected_cb(self.curdir, self.recursive_check.get_active())
        self.close()
        
    def on_cancel_clicked(self, button):
        self.close()

    def close(self):
        self.loader.stop()
        self.loader.join()
        # This is to avoid invoking self.window.destroy() directly, it
        # was causing a SIGSEGV after the on_cursor_changed handler ran
        # (https://mail.gnome.org/archives/gtk-app-devel-list/2004-September/msg00230.html)
        gobject.idle_add(lambda window: window.destroy(), self.window)