Page 3 of 4 FirstFirst 1234 LastLast
Results 21 to 30 of 38

Thread: Extracting data from recently-used.xbel?

  1. #21
    Join Date
    Aug 2018
    Location
    England
    Beans
    51
    Distro
    Xubuntu

    Re: Extracting data from recently-used.xbel?

    You have made some good improvements. Keeping the path and date separate by using a key function for the sort is good style. What I do not like is your use of a dictionaries. A more appropriate language construct is the named tuple. I have modified the code to use a named tuple in place of the two dictionaries. You have widened the window and I have made it deeper.

    Code:
    import os
    from xml.dom import minidom
    from collections import namedtuple
    from datetime import date
    import urllib.parse
    import subprocess
    from tkinter import *
    
    # Do not show paths that were modified more than MaxDays before today.
    MaxDays = 100
    
    pathdate = namedtuple('pathdate', ['path', 'date'])
    
    # Extract the directory path names and modification dates from the recently-used.xbel file
    mydom=minidom.parse(os.environ['HOME'] + '/.local/share/recently-used.xbel')
    bookmarkNodeList=mydom.getElementsByTagName('bookmark')
    PathDate = []
    for bookmarkNode in bookmarkNodeList:
        filepath=bookmarkNode.getAttribute('href')
        moddate=bookmarkNode.getAttribute('modified')
        if os.path.exists(urllib.parse.unquote(filepath)[7:]):
            PathDate.append(pathdate(os.path.dirname(filepath), moddate))
    
    def keyfunc(item):
        return item.path + item.date
    
    # Sort into descending order of path and then modification date.
    PathDate.sort(reverse=True,key=keyfunc)
    
    # Remove old or duplicate rows.
    Pruned = []
    PrevPath = ''
    for row in PathDate:
        Path = row.path
        Year = int(row.date[0:4])
        Month = int(row.date[5:7])
        Day = int(row.date[8:10])
        Date = date(Year, Month, Day)
        DeltaDays = (date.today() - Date).days
        if Path != PrevPath and DeltaDays <= MaxDays:
            Pruned.append(Path)
            PrevPath = Path
    
    # Event handler for a left mouse click on a list box entry.
    def on_click_listbox(event):
        # Get the selected line index tuple.
        index = listbox.curselection()
        # Open Thunar for the selected path name.
        subprocess.Popen(['thunar', Pruned[index[0]]])
    
    # Create the root window.
    root = Tk()
    root.title("Recently Accessed Folders")
    
    # Create a Listbox and attach it to the left side of the root window.
    listbox = Listbox(root, height = 30, width = 100)
    listbox.pack(side = LEFT, fill = BOTH)
    
    # Create a Scrollbar and attach it to the right side of the root window.
    scrollbar = Scrollbar(root)
    scrollbar.pack(side = RIGHT, fill = BOTH)
    scrollbar.config(command = listbox.yview)
    
    # Attach the Listbox to the Scrollbar.
    listbox.config(yscrollcommand = scrollbar.set)
    
    # Bind a left mouse click event to the listbox.
    listbox.bind('<ButtonRelease-1>', on_click_listbox)
    
    # Insert the path names with special characters into the listbox.
    for Path in Pruned:
        listbox.insert(END, urllib.parse.unquote(Path))
    
    root.mainloop()
    I have spotted an issue. I visited some icon directories in /usr/share, but did not change anything. They are in the .xbel file with a modified date. Very strange.

  2. #22
    Join Date
    Dec 2014
    Beans
    1,602

    Re: Extracting data from recently-used.xbel?

    As I said, I use python rarely. Hadn't even heard of named tuples ... but you're right, it looks cleaner. Actually using a sort function was born out of my desire not to mix separate pieces of data together because I don't like unscrambling eggs, so the dictionary came first and then the sort function was a matter of necessity.

    I'd think the modification date is the modification date of the entry, not the file. So since you opened the directory, a new entry was created with a modification date of today ...

    Five more small changes:

    • I like array variables to have names in plural form, since they hold many things. So I changed PathDate to PathDates. Just a stylistic thing. Also reduces the probability of mixing up pathdate the named tuple and PathDates the array.
    • Import of the parser from dateutil to get rid of the slicing and dicing of the timestamp and simply convert that into a datetime (and that into a date).
    • Find the longest path and use that to set the listbox width.
    • Keyboard control: Set the focus to the listbox, bind the Return key to select the current element and ^q to quit.
    • Set the listbox to expand if the window is resized.


    Code:
    import os
    from xml.dom import minidom
    from collections import namedtuple
    from datetime import date
    from dateutil import parser
    import urllib.parse
    import subprocess
    from tkinter import *
    
    # Do not show paths that were modified more than MaxDays before today.
    MaxDays = 100
    
    # find max path-length for width of listbox
    MaxPathLength = 0
    
    # named tuple for storage of the entries
    pathdate = namedtuple('pathdate',['path','date'])
    
    # Extract the directory path names and modification dates from the recently-used.xbel file
    
    mydom=minidom.parse(os.environ['HOME'] + '/.local/share/recently-used.xbel')
    bookmarkNodeList=mydom.getElementsByTagName('bookmark')
    PathDates = []
    for bookmarkNode in bookmarkNodeList:
        filepath=bookmarkNode.getAttribute('href')
        moddate=bookmarkNode.getAttribute('modified')
        fp=urllib.parse.unquote(filepath)[7:]
        if os.path.exists(fp):
            if len(fp) > MaxPathLength:
                MaxPathLength = len(fp)
            PathDates.append(pathdate(os.path.dirname(filepath),moddate))
    
    # sort needs a function to give it a key if the list contains complex elements
    def keyfunc(item):
        return item.path + item.date
    
    # Sort into descending order of path and then modification date.
    PathDates.sort(reverse=True,key=keyfunc)
    
    # Remove old or duplicate rows.
    Pruned = []
    PrevPath = ''
    for row in PathDates:
        Path = row.path
        # using parser from dateutils to get a datetime from the timestamp string
        # and then turning that into a date
        DeltaDays = (date.today() - parser.parse(row.date).date()).days
        if Path != PrevPath and DeltaDays <= MaxDays:
            Pruned.append(Path)
            PrevPath = Path
    
    # Event handler for a left mouse click on a list box entry.
    def on_click_listbox(event):
        # Get the selected line index tuple.
        index = listbox.curselection()
        # Open Thunar for the selected path name.
        subprocess.Popen(['thunar', Pruned[index[0]]])
    
    # Event Handler for ^q=quit
    def finish(event):
        quit()
    
    # Create the root window.
    root = Tk()
    root.title("Recently Accessed Folders")
    
    # Create a Listbox and attach it to the left side of the root window
    # MaxPathLength as width is too much since it assumes a fixed-width font, multiplying by .7 seems to give a good result
    listbox = Listbox(root, height = 30, width = int(MaxPathLength * 0.7))
    
    # Insert the path names with special characters into the listbox.
    for Path in Pruned:
        listbox.insert(END, urllib.parse.unquote(Path))
    
    listbox.pack(expand=1, side = LEFT, fill = BOTH)
    
    # Create a Scrollbar and attach it to the right side of the root window.
    scrollbar = Scrollbar(root)
    scrollbar.pack(side = RIGHT, fill = BOTH)
    scrollbar.config(command = listbox.yview)
        
    # Attach the Listbox to the Scrollbar.
    listbox.config(yscrollcommand = scrollbar.set)
    
    # Bind a left mouse click event and a Return key to the listbox. 
    listbox.bind('<ButtonRelease-1>', on_click_listbox)
    listbox.bind('<Return>',on_click_listbox)
    # Bind Ctrl-q to finish
    listbox.bind('<Control-q>',finish)
    listbox.set_focus()
    
    root.mainloop()
    One nitpick remaining, making the window narrower makes the scrollbar vanish. Probably something to do with the pack layout-manager.

    Holger

  3. #23
    Join Date
    Aug 2018
    Location
    England
    Beans
    51
    Distro
    Xubuntu

    Re: Extracting data from recently-used.xbel?

    Yes, that is much improved. One little problem though. Listbox does not have a set_focus attribute. I have changed that to focus_set, which does work.

    Code:
    import os
    from xml.dom import minidom
    from collections import namedtuple
    from datetime import date
    from dateutil import parser
    import urllib.parse
    import subprocess
    from tkinter import *
    
    # Do not show paths that were modified more than MaxDays before today.
    MaxDays = 100
    
    # find max path-length for width of listbox
    MaxPathLength = 0
    
    # named tuple for storage of the entries
    pathdate = namedtuple('pathdate',['path','date'])
    
    # Extract the directory path names and modification dates from the recently-used.xbel file
    mydom=minidom.parse(os.environ['HOME'] + '/.local/share/recently-used.xbel')
    bookmarkNodeList=mydom.getElementsByTagName('bookmark')
    PathDates = []
    for bookmarkNode in bookmarkNodeList:
        filepath=bookmarkNode.getAttribute('href')
        moddate=bookmarkNode.getAttribute('modified')
        fp=urllib.parse.unquote(filepath)[7:]
        if os.path.exists(fp):
            if len(fp) > MaxPathLength:
                MaxPathLength = len(fp)
            PathDates.append(pathdate(os.path.dirname(filepath),moddate))
    
    # sort needs a function to give it a key if the list contains complex elements
    def keyfunc(item):
        return item.path + item.date
    
    # Sort into descending order of path and then modification date.
    PathDates.sort(reverse=True,key=keyfunc)
    
    # Remove old or duplicate rows.
    Pruned = []
    PrevPath = ''
    for row in PathDates:
        Path = row.path
        # using parser from dateutils to get a datetime from the timestamp string
        # and then turning that into a date
        DeltaDays = (date.today() - parser.parse(row.date).date()).days
        if Path != PrevPath and DeltaDays <= MaxDays:
            Pruned.append(Path)
            PrevPath = Path
    
    # Event handler for a left mouse click on a list box entry.
    def on_click_listbox(event):
        # Get the selected line index tuple.
        index = listbox.curselection()
        # Open Thunar for the selected path name.
        subprocess.Popen(['thunar', Pruned[index[0]]])
    
    # Event Handler for ^q=quit
    def finish(event):
        quit()
    
    # Create the root window.
    root = Tk()
    root.title("Recently Accessed Folders")
    
    # Create a Listbox and attach it to the left side of the root window
    # MaxPathLength as width is too much since it assumes a fixed-width font, multiplying by .7 seems to give a good result
    listbox = Listbox(root, height = 30, width = int(MaxPathLength * 0.7))
    # Insert the path names with special characters into the listbox.
    for Path in Pruned:
        listbox.insert(END, urllib.parse.unquote(Path))
    listbox.pack(expand=1, side = LEFT, fill = BOTH)
    
    # Create a Scrollbar and attach it to the right side of the root window.
    scrollbar = Scrollbar(root)
    scrollbar.pack(side = RIGHT, fill = BOTH)
    scrollbar.config(command = listbox.yview)
        
    # Attach the Listbox to the Scrollbar.
    listbox.config(yscrollcommand = scrollbar.set)
    
    # Bind a left mouse click event and a Return key to the listbox. 
    listbox.bind('<ButtonRelease-1>', on_click_listbox)
    listbox.bind('<Return>',on_click_listbox)
    # Bind Ctrl-q to finish
    listbox.bind('<Control-q>',finish)
    listbox.focus_set()
    
    root.mainloop()
    I do not write much Python either. I wrote a few little programs a few years ago. I have to keep looking up the syntax. Named tuples are pretty fundamental - the equivalent of a record in Pascal or a struct in C. It did not take me long to look for the Python equivalent. I can remember the syntax for Fortran IV, with anything else I am struggling.

  4. #24
    Join Date
    Aug 2018
    Location
    England
    Beans
    51
    Distro
    Xubuntu

    Re: Extracting data from recently-used.xbel?

    The other refinement that I had considered was to make Pruned store both the path and date rather than just the date. We could then put a text box in the window to change MaxDays and re-display the paths with that date as a filter.

  5. #25
    Join Date
    Aug 2018
    Location
    England
    Beans
    51
    Distro
    Xubuntu

    Re: Extracting data from recently-used.xbel?

    I have modified the program to include a text box for setting the maximum number of days before the current day for which accesses are to be displayed.

    Code:
    import osfrom xml.dom import minidom
    from collections import namedtuple
    from datetime import date
    from dateutil import parser
    import urllib.parse
    import subprocess
    from tkinter import *
    
    # Do not show paths that were modified more than MaxDays before today.
    MaxDays = 90
    
    # find max path-length for width of listbox
    MaxPathLength = 0
    
    # Named tuple for storage of entries.
    pathdate = namedtuple('pathdate',['path','date'])
    
    # Extract the directory path names and modification dates from the recently-used.xbel file
    mydom=minidom.parse(os.environ['HOME'] + '/.local/share/recently-used.xbel')
    bookmarkNodeList=mydom.getElementsByTagName('bookmark')
    PathDates = []
    for bookmarkNode in bookmarkNodeList:
        filepath=bookmarkNode.getAttribute('href')
        moddate=bookmarkNode.getAttribute('modified')
        fp=urllib.parse.unquote(filepath)[7:]
        if os.path.exists(fp):
            if len(fp) > MaxPathLength:
                MaxPathLength = len(fp)
            PathDates.append(pathdate(os.path.dirname(filepath),moddate))
    
    # sort needs a function to give it a key if the list contains complex elements
    def keyfunc(item):
        return item.path + item.date
    
    # Sort into descending order of path and then modification date.
    PathDates.sort(reverse=True,key=keyfunc)
    
    # Remove old or duplicate rows.
    Pruned = []
    PrevPath = ''
    for row in PathDates:
        Path = row.path
        if Path != PrevPath:
            # Use the parser from dateutils to get a datetime from the timestamp string
            # and then turn that into a date.
            Date = parser.parse(row.date).date()
            Pruned.append(pathdate(Path,Date))
            PrevPath = Path
    
    # Event handler for <CR> in the entry field.
    def on_return_entry(event):
        global MaxDays
        try:
            MaxDays = int(event.widget.get())
        except:
            Maxdays = 90
        fill_listbox()
    
    # Event handler for a left mouse click on a list box entry.
    def on_click_listbox(event):
        # Get the selected line index tuple.
        index = listbox.curselection()
        # Open Thunar for the selected path name.
        subprocess.Popen(['thunar', Pruned[index[0]].path])
    
    # Event Handler for ^q=quit
    def finish(event):
        quit()
        
    # Create the root window.
    root = Tk()
    root.title("Recently Accessed Folders")
    
    # Create a Frame at the bottom of the root window.
    frame = Frame(root)
    frame.pack(side=BOTTOM)
    
    # Create a text label on the left of the Frame.
    label = Label(frame, text='Maximum days accessed before today')
    label.pack(side=LEFT)
    
    # Create an entry field on the right of the Frame.
    entry = Entry(frame, width=5)
    entry.insert(0, '90')
    entry.bind('<Return>', on_return_entry)
    entry.pack(side=RIGHT)
    
    # Create a Listbox and attach it to the left side of the root window
    # MaxPathLength as width is too much since it assumes a fixed-width font,
    # multiplying by .7 seems to give a good result.
    listbox = Listbox(root, height = 30, width = int(MaxPathLength * 0.7))
    listbox.pack(expand=1, side = LEFT, fill = BOTH)
    
    def fill_listbox():
        # If the date is within range, fill the list box with the path names with special characters.
        listbox.delete(0, END)
        for row in Pruned:
            DeltaDays = (date.today() - row.date).days
            if DeltaDays <= MaxDays:
                listbox.insert(END, urllib.parse.unquote(row.path))
    
    fill_listbox()
    
    # Create a Scrollbar and attach it to the right side of the root window.
    scrollbar = Scrollbar(root)
    scrollbar.pack(side = RIGHT, fill = BOTH)
    scrollbar.config(command = listbox.yview)
        
    # Attach the Listbox to the Scrollbar.
    listbox.config(yscrollcommand = scrollbar.set)
    
    # Bind a left mouse click event and a Return key to the listbox. 
    listbox.bind('<ButtonRelease-1>', on_click_listbox)
    listbox.bind('<Return>',on_click_listbox)
    # Bind Ctrl-q to finish
    listbox.bind('<Control-q>',finish)
    listbox.focus_set()
    
    root.mainloop()
    Everything seems to work, but I am not convinced that the "modified" dates in the .xbel file make a lot of sense.
    Last edited by geofftf; 4 Weeks Ago at 08:15 PM. Reason: Changed caption

  6. #26
    Join Date
    Sep 2020
    Beans
    9

    Re: Extracting data from recently-used.xbel?

    Geoff and Holger, this is stunning. I can't believe you guys managed to program the exact thing!

    I tried to figure three things out and managed one: to sort by date, I swapped 'date' and 'path' in this line, resulting in: return item.date + item.path. Correct? But when I do this, the duplicate rows all show up again, I don't know why.

    Now, for the two other things I couldn't solve: how would you display only the folder names instead of the full path? And how would you set a limit to the number of items in the list? (it seems this intention is un-pythonesque from what I gather)

  7. #27
    Join Date
    Aug 2018
    Location
    England
    Beans
    51
    Distro
    Xubuntu

    Re: Extracting data from recently-used.xbel?

    The purpose of the sort in the program is to remove duplicate path names. It sorts in descending order of path, and then date. Each group of duplicate paths is then together in the list, in descending order of date. For each group of duplicate paths, the program then selects the first entry for each path from the list, which will be the one with the most recent date. If you reverse the path and date this algorithm will not work.

    I will modify the program.

    Geoff.

  8. #28
    Join Date
    Aug 2018
    Location
    England
    Beans
    51
    Distro
    Xubuntu

    Re: Extracting data from recently-used.xbel?

    I have modified the program to display the paths is descending order of the last access date.

    Code:
    import os
    from xml.dom import minidom
    from collections import namedtuple
    from datetime import date
    from dateutil import parser
    import urllib.parse
    import subprocess
    from tkinter import *
    
    # Do not show paths that were modified more than MaxDays before today.
    MaxDays = 90
    
    # find max path-length for width of listbox
    MaxPathLength = 0
    
    # Named tuple for storage of entries.
    pathdate = namedtuple('pathdate',['path','date'])
    
    # Extract the directory path names and modification dates from the recently-used.xbel file
    
    mydom=minidom.parse(os.environ['HOME'] + '/.local/share/recently-used.xbel')
    bookmarkNodeList=mydom.getElementsByTagName('bookmark')
    PathDates = []
    for bookmarkNode in bookmarkNodeList:
        filepath=bookmarkNode.getAttribute('href')
        moddate=bookmarkNode.getAttribute('modified')
        fp=urllib.parse.unquote(filepath)[7:]
        if os.path.exists(fp):
            if len(fp) > MaxPathLength:
                MaxPathLength = len(fp)
            PathDates.append(pathdate(os.path.dirname(filepath),moddate))
    
    # Sort PathDates into descending order of path and then modification date.
    
    # Sort needs a function to give it a key because the list contains complex elements.
    def pd_keyfunc(item):
        return item.path + item.date
    
    PathDates.sort(reverse=True,key=pd_keyfunc)
    
    # Each group of duplicate paths is now together in the list, in descending order of date.
    # For each group of duplicate paths, select the first entry for each path from the list,
    # which will be the one with the most recent date.
    Pruned = []
    PrevPath = ''
    for row in PathDates:
        Path = row.path
        if Path != PrevPath:
            # Use the parser from dateutils to get a datetime from the timestamp string,
            # and then turn that into a date.
            Date = parser.parse(row.date).date()
            Pruned.append(pathdate(Path,Date))
            PrevPath = Path
    
    # Sort Pruned into descending order of date.
    
    # Sort needs a function to give it a key because the list contains complex elements.
    def p_keyfunc(item):
        return item.date
    
    Pruned.sort(reverse=True,key=p_keyfunc)
    
    # Event handler for <CR> in the entry field.
    def on_return_entry(event):
        global MaxDays
        try:
            print(int(event.widget.get()))
            MaxDays = int(event.widget.get())
        except:
            entry.delete(0, END)
            entry.insert(0, 'Error')
        fill_listbox()
    
    # Event handler for a left mouse click on a list box entry.
    def on_click_listbox(event):
        # Get the selected line index tuple.
        index = listbox.curselection()
        # Open Thunar with the selected path name.
        subprocess.Popen(['thunar', Pruned[index[0]].path])
    
    # Event Handler for ^q=quit
    def finish(event):
        quit()
        
    # Create the root window.
    root = Tk()
    root.title("Recently Accessed Folders")
    
    # Create a Frame at the bottom of the root window.
    frame = Frame(root)
    frame.pack(side=BOTTOM)
    
    # Create a text label on the left of the Frame.
    label = Label(frame, text='Maximum days folder accessed before today')
    label.pack(side=LEFT)
    
    # Create an entry field on the right of the Frame.
    entry = Entry(frame, width=5)
    entry.insert(0, str(MaxDays))
    entry.bind('<Return>', on_return_entry)
    entry.pack(side=RIGHT)
    
    # Create a Listbox and attach it to the left side of the root window.
    # Using MaxPathLength as the width is too much because it assumes a fixed-width font.
    # Multiplying by .7 seems to give a good result.
    listbox = Listbox(root, height = 30, width = int(MaxPathLength * 0.7))
    listbox.pack(expand=1, side = LEFT, fill = BOTH)
    
    # Fill the listbox with the path names with special characters, when the date is within range. 
    def fill_listbox():
        listbox.delete(0, END)
        for row in Pruned:
            listbox.insert(END, urllib.parse.unquote(row.path[7:]))
            if (date.today() - row.date).days > MaxDays:
                break
    
    fill_listbox()
    
    # Create a Scrollbar and attach it to the right side of the root window.
    scrollbar = Scrollbar(root)
    scrollbar.pack(side = RIGHT, fill = BOTH)
    scrollbar.config(command = listbox.yview)
        
    # Attach the Listbox to the Scrollbar.
    listbox.config(yscrollcommand = scrollbar.set)
    
    # Bind a left mouse click event and a Return key to the listbox. 
    listbox.bind('<ButtonRelease-1>', on_click_listbox)
    listbox.bind('<Return>',on_click_listbox)
    # Bind Ctrl-q to finish
    listbox.bind('<Control-q>',finish)
    listbox.focus_set()
    
    root.mainloop()
    Xubuntu has directories with the same name in many places. Some users will want the full path name. I am not sure how to tackle that one. Try the modified program and see what you think.

    Do we need to text box at the bottom of the window now that we are sorting into descending order of access date? If the user does not want to look at files that were not accessed recently, he does not have to scroll down that far. Adding the text box and its label complicates the program and makes it a little messy, because of the order in which the widgets have to be packed. Would it be helpful to show the access dates at the beginning of each line?
    Last edited by geofftf; 4 Weeks Ago at 10:23 AM.

  9. #29
    Join Date
    Sep 2020
    Beans
    9

    Re: Extracting data from recently-used.xbel?

    This is really great stuff!

    This display is perfect for me and definitely makes things clearer: I can find a folder exactly where I expect it, because it shows in which order I worked. I believe this is also the reason why in Windows, the same kind of menu would only display folder names - even with the same name, the order would tell them apart easily; whereas since the end folders are not in a column, it takes me some time finding the end of each line to figure them out. Then again, maybe I'm not as used to heavier visual information as more experienced xubuntu users!

    For the text box: to me, this program is more of a day-by-day need for the last 8-10 folders. Even with a good list of bookmarks, it allows getting back to the deeper, remote ones I'm working on at the moment. This is why this display is really good! I wouldn't personally need the access dates or the text box.

    Now, something I noticed, the list doesn't refresh when using new folders, so closing and re-opening the list window is necessary to see the newer results. Do you think it would it be possible closing it upon folder opening for example? That would work for me. Again, I tried figuring this out in python, thinking it would not be too complicated haha - I also tried setting up a 'close & open again' shortcut using xdotool, but didn't manage to both the commands working in one go.

    EDIT: would you know how to make the folders open with nemo (default file manager) rather than the native file manager?
    Last edited by erza-k-rot; 4 Weeks Ago at 03:20 PM.

  10. #30
    Join Date
    Aug 2018
    Location
    England
    Beans
    51
    Distro
    Xubuntu

    Re: Extracting data from recently-used.xbel?

    What I can do is add a refresh button to the screen. If you press that, program would re-read the .xbel file and generate a fresh list of file paths. Does that sound helpful? I could also provide radio buttons for "Folder Name" and "Folder Path". Would that be helpful?

    The MaxDays text box is not doing much now, so I can remove it. It does not even help efficiency, unless the date filter is applied on the first pass through the .xbel file, and we do not appear to have a performance issue anyway.

Page 3 of 4 FirstFirst 1234 LastLast

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •