How does one add an item to GTK's "recently used" file list from Python?
Asked Answered
C

2

21

I'm trying to add to the "recently used" files list from Python 3 on Ubuntu.

I am able to successfully read the recently used file list like this:

from gi.repository import Gtk
recent_mgr = Gtk.RecentManager.get_default()
for item in recent_mgr.get_items():
    print(item.get_uri())

This prints out the same list of files I see when I look at "Recent" in Nautilus, or look at the "Recently Used" place in the file dialog of apps like GIMP.

However, when I tried adding an item like this (where /home/laurence/foo/bar.txt is an existing text file)...

recent_mgr.add_item('file:///home/laurence/foo/bar.txt')

...the file does not show up in the Recent section of Nautilus or in file dialogs. It doesn't even show up in the results returned by get_items().

How can I add a file to GTK's recently used file list from Python?

Chesterchesterfield answered 3/10, 2016 at 17:0 Comment(10)
@oldtechaa The list of recent files seen by Nautilus and the file dialog in other apps (including GIMP, Inkscape, and even Chrome) seem to be identical. Many apps default to filtering the list to their own file type, but if you change the filter to "All Files" you see exactly the same list that Nautilus shows, and that is displayed by the code in the question.Chesterchesterfield
What does add_item() return?Legation
@Legation Calling add_item() returns True. (Actually, is seems to always return True, even if the URI I pass to it is nonexistant or complete nonsense. The only exception is if I pass it a non-str. Then it raises TypeError.)Chesterchesterfield
Maybe not though seeing that add_item() returns true would suggest it is something else. Could it be picking up the wrong history file some how? If it returns true it suggests it succeeded in writing it somewhere.Maund
The real question I have, which is unrelated to your code, is why add_item() doesn't use GError while every other method of GtkRecentManager does. I'll ask that later; maybe one of the GTK+ developers can guess what's going on...Legation
@Legation This is due to the use of asynchronous code to avoid g_content_type_guess to block the flow of the program I think, see my answer below.Confectionery
@JacquesGaudin good to know then; thanks. And it seems add_full() is written in a way that even though it is synchronous, it cannot fail unless the input parameters are wrong. I'm not sure if this is smart, but oh well :/Legation
@Legation Not sure about the smartness of it either, especially when you realise that g_content_type_guess worst guess is the first mimetype in the system's mimetype list. Not a very smart guess !Confectionery
Actually I should rephrase that: "it will hide failures of any of its underlying functions as it doesn't call the versions of those functions that could potentially report failures (if they even exist)"; I'd have to look at the GBookmarkFile API. Also, what would you suggest as a better worst-case, application/octet-stream?Legation
@Legation I may be a bit naive but I was thinking that there was a mimetype for unknown data file. The content type guess returns an uncertainty boolean which could be replaced with an unknown data mimetype linking to a hexadecimal editor maybe...Confectionery
C
18

A Gtk.RecentManager needs to emit the changed signal for the update to be written in a private attribute of the C++ class. To use a RecentManager object in an application, you need to start the event loop by calling Gtk.main:

from gi.repository import Gtk

recent_mgr = Gtk.RecentManager.get_default()
uri = r'file:/path/to/my/file'
recent_mgr.add_item(uri)
Gtk.main()

If you don't call Gtk.main(), the changed signal is not emitted and nothing happens.

To answer @andlabs query, the reason why RecentManager.add_item returns a boolean is because the g_file_query_info_async function is called. The callback function gtk_recent_manager_add_item_query_info then gathers the mimetype, application name and command into a GtkRecentData struct and finally calls gtk_recent_manager_add_full. The source is here.

If anything goes wrong, it is well after add_item has finished, so the method just returns True if the object it is called from is a RecentManager and if the uri is not NULL; and False otherwise.

The documentation is inaccurate in saying:

Returns TRUE if the new item was successfully added to the recently used resources list

as returning TRUE only means that an asynchronous function was called to deal with the addition of a new item.

As suggested by Laurence Gonsalves, the following runs pseudo-synchronously:

from gi.repository import Gtk, GObject

recent_mgr = Gtk.RecentManager.get_default()
uri = r'file:/path/to/my/file'
recent_mgr.add_item(uri)
GObject.idle_add(Gtk.main_quit)
Gtk.main()
Confectionery answered 7/10, 2016 at 23:53 Comment(0)
P
1

This is my solution (complet script) with timer for quit GTK.main() loop

#!/usr/bin/env python3

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib
import sys
import os
import subprocess

recent_mgr = Gtk.RecentManager.get_default()

if len(sys.argv) <= 1:
    paths = (os.getcwd(),)
else:
    paths = sys.argv[1:]

for path in paths:
    if os.path.exists(path):
        if path[0] != "/":
            path = os.getcwd() + "/" + path
        subprocess.call(["touch", "-a", path])
        uri = r"file:" + path
        recent_mgr.add_item(uri)

GLib.timeout_add(22, Gtk.main_quit, None)
Gtk.main()

Phalarope answered 8/9, 2021 at 18:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.