Drop file in Python GUI (GTK)

Question:

I would like to write a little app who takes a selected number of file, and store the path of everything for future things.

Basically:

Select mixed (images, audio, video) file from Nautilus (in this case), drag them, drop them in this GUI and get the absolute path of every element to write in a list, for example.

The program itself isn’t a problem at all, but i fail in the most simple task, maybe: creating a GUI (i’ve choosed GTK, but everything will be fine, just need the job done) who accept the elements and store them.

I’m playing around with Glade, but i’m not even sure if is this the right choice.

Can someone help me with the build of this GUI or by pointing out some resource?

Asked By: Reg

||

Answers:

Heres a good way to get started with Glade, Gtk and Python:

http://python-gtk-3-tutorial.readthedocs.io/en/latest/builder.html

And drag and drop:

http://python-gtk-3-tutorial.readthedocs.io/en/latest/drag_and_drop.html

Edit with a small working program:

#!/usr/bin/env python

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
import os, sys



class GUI:
    def __init__(self):
        window = Gtk.Window()
        window.connect('destroy', Gtk.main_quit)
        textview = Gtk.TextView()
        enforce_target = Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags(4), 129)
        textview.drag_dest_set(Gtk.DestDefaults.ALL, [enforce_target], Gdk.DragAction.COPY)
        textview.connect("drag-data-received", self.on_drag_data_received)
        #textview.drag_dest_set_target_list([enforce_target])
        window.add(textview)
        window.show_all()

    def on_drag_data_received(self, widget, drag_context, x,y, data,info, time):
        print (data.get_text())

def main():
    app = GUI()
    Gtk.main()
        
if __name__ == "__main__":
    sys.exit(main())
Answered By: theGtknerd

I’ve tried the example provided by @theGtknerd to no avail. After hours of debugging, I got this example working with a few lines.

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
import sys

class GUI:
    def __init__(self):
        window = Gtk.Window()
        window.connect('destroy', Gtk.main_quit)

        textview = Gtk.TextView()
        #                      (drag behaviour, targets (leave empty), action)
        textview.drag_dest_set(Gtk.DestDefaults.DROP, [], Gdk.DragAction.COPY)
        # add the ability to receive URIs (e.g. file paths)
        textview.drag_dest_add_uri_targets()
        textview.connect("drag-data-received", self.on_drag_data_received)

        window.add(textview)
        window.show_all()

    def on_drag_data_received(self, widget, drag_context, x,y, data,info, time):
        print(data.get_uris())
        #              (context, success, delete_from_source, time)
        Gtk.drag_finish(drag_context, True, False, time)
        # always call Gtk.drag_finish to receive as suggested by documentation


def main():
    GUI()
    Gtk.main()

if __name__ == "__main__":
    sys.exit(main())

Own observations:

  • Any Gtk.Widget can be enabled to receive data with .drag_dest_set() but needs to set the targets either here or calling methods that adds them like .drag_dest_add_uri_targets().
  • Gtk.TextView seems to have a bug where signal drag-data-received repeats twice after the very first call, thus the handler gets called twice. In the pair of calls, the first one contains new data and the second one contains the previous data.
Answered By: sirDeniel

To avoid drag-data-received being called twice…

You can drop from Nautilus, LibreOffice, gedit, a link fron Chrome

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
import sys, urllib

class GUI:
    def __init__(self):
        window = Gtk.Window()
        window.connect('destroy', Gtk.main_quit)

        textview = Gtk.TextView()

        # 'enforce_target1'
        enforce_target1 = Gtk.TargetEntry.new('text/uri-list', 0, 50)  # laisse passer les noms de fichiers avec pb d'accents (info = 50)      
        # add the ability to receive URIs (e.g. file paths)
        textview.drag_dest_set_target_list([enforce_target1])

        # add the ability to receive 'texts', 'link'
        textview.drag_dest_add_text_targets()       # gère correctement les textes, les liens (info = 0)

        # connect the signal "drag-data-received"  of the textview to the callback 'self.on_drag_data_received'
        textview.connect("drag-data-received", self.on_drag_data_received)

        window.add(textview)
        window.show_all()

    def on_drag_data_received(self, widget, drag_context, x,y, data,info, time):
        print(info)

        if info == 50:        # 'text/uri-list' from Nautilus
            data = data.get_data()
            # Si on a glissé un fichier depuis Nautilus :            
            # L'objet byte se termine par rn, il faut supprimer ces deux caractères
            # r correspond à CR (Carriage Return) n correspond à LF (Line Feed).
            if data[-2:] == b'rn':
                    print("     ...se termine par \r CR (Carriage Return) et \n correspond à LF (Line Feed)")
                    data  = data[:-2]
                    print(f"Data reçu : {data}")
            # Replacing '%20' with Space and  '%C3%A9' with é .... etc....
            data=urllib.parse.unquote(data.decode())         
            print(f"Data reçu  (unquote) : {data}")
            # Insert data in the the textview
            widget.get_buffer().insert_at_cursor(data)

        if info == 0 :
            print(data.get_text())
            print("The drop is done automatically on the textview")
            
        # Gtk.drag_finish(drag_context, True, False, time)
        # always call Gtk.drag_finish to receive as suggested by documentation

def main():
    GUI()
    Gtk.main()

if __name__ == "__main__":
    sys.exit(main())
Answered By: JEAN-PAUL Thiel
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.