Python qt: copy/paste and drag/drop

From wikinotes


MimeData

Links
list of formally accepted mimetypes http://www.iana.org/assignments/media-types/media-types.xhtml
Mimetype Naming Conventions
officially recognized mimetypes application/json
non-official mimetypes x- prefix application/x-yaml
platform-specific mimetypes See QMimedata application/x-qt-windows-mime;value="<custom type>"

mimetypes describe the type of data a file contains. On unix-based platforms, these mimetypes can be associated with programs that should be used to open them.

This information is also used when pasting items from the clipboard. During a paste operation, your clipboard's content's mimetype in the active window. If you program knows how to handle this mimetype, the paste is accepted, and it is pasted into the program. Otherwise it is silently rejected.

QtCore.QMimeData is Qt's object for storing mimetype-aware data. To maximize the number of programs that can handle your data, you may want to express the same data in a few different formats (ex: 'text/plain', 'application/json', 'application/yaml', ...etc...).

from Qt import QtCore

data = {'name': 'will', 'age': 32}
qmimedata = QtCore.QMimeData()
qmimedata.setData('text/plain', data['name'])
qmimedata.setData('application/json', json.dumps(data))
qmimedata.setData('application/yaml', yaml.dumps(data))


For more information, see mimetypes .

Copy/Paste

example widget implementing both copy/paste

from Qt import QtWidgets, QtCore, QtGui


class CopyPasteWidget(QtWidgets.QLabel):
    def __init__(self):
        super(CopyPasteWidget, self).__init__(
            'Try Ctrl+C/Ctrl+V on this widget.<br>'
            'Copied data can also be pasted into other programs, and vice-versa'
        )
        self._create_actions()

    def _create_actions(self):
        # create actions
        self.copy_action = QtWidgets.QAction('Copy', parent=self)
        self.paste_action = QtWidgets.QAction('Paste', parent=self)

        # add actions to widget (widget watches for keysequence)
        self.addAction(self.copy_action)
        self.addAction(self.paste_action)

        # set shortcuts 
        # NOTE (QKeySequence.StandardKey entries are platform-specific. confirm they are bound!)
        self.copy_action.setShortcut(QtGui.QKeySequence.Copy)
        self.paste_action.setShortcut(QtGui.QKeySequence.Paste)

        # connections
        self.copy_action.triggered.connect(self._handle_copy)
        self.paste_action.triggered.connect(self._handle_paste)

    def _handle_copy(self, *args, **kwargs):
        qmimedata = QtCore.QMimeData()
        clipboard = QtWidgets.QApplication.clipboard()

        copy_data = 'plaintext data'.encode('utf-8')  # your data, as a bytestr
        qmimedata.setData('text/plain', QtCore.QByteArray(copy_data))

        clipboard.clear()
        clipboard.setMimeData(qmimedata)
        print('copied data: "{}"'.format(copy_data))

    def _handle_paste(self, *args, **kwargs):
        clipboard = QtWidgets.QApplication.clipboard()
        qmimedata = clipboard.mimeData()

        if qmimedata.hasFormat('text/plain'):
            copy_data = qmimedata.data('text/plain')
            print('pasted data: "{}"'.format(copy_data))
        else:
            print('paste ignored')

NOTE:
mimetypes on windows/osx do not always match exactly with the open standards.

See QWinMime and QMacPasteboardMime for conversions to/from the open mimetype standards.

Drag/Drop (widgets)

Dragging and Dropping Widgets require:

  • A Customized Drop Widget Class
  • A Customized Drag Widget Class

Note that you can always access the dropped object from the object receiving it with event.source(). Drop Events cannot be made on layouts, but they CAN be made on QWidgets containing layouts and several other widgets.

Drag Widget

class DragButton(QtGui.QPushButton):
	def __init__(self, *args, **kwds ):
		super(DragButton, self).__init__(*args, **kwds)

	def mouseMoveEvent(self, event):
		"""
		MimeData allows you to create custom mimetypes to be used to validate 
		the dataType on the other end. These can be nonsense to ensure that
		you are actually getting the info you want.

		Or, if it is simply text, images, or some other objectType you want to be
		reusable, across different windows, you can make them all use consistent
		mimetypes.
		"""
		if event.buttons() != QtCore.Qt.LeftButton:
			return

		mimeData = QtCore.QMimeData()
		mimeData.setData('application/x-interpipeline', 'neato')

		drag = QtGui.QDrag(self)
		drag.setMimeData(mimeData)

		drag.exec_()

Drop Widget

class DropTextEdit( QtGui.QTextEdit ):

	def __init__( self, *args, **kwds ):
		super( DropTextEdit, self ).__init__( *args, **kwds )
		self.setAcceptDrops(1)						## Make sure to actually accept drops


	def dragEnterEvent( self, event ):
		"""
		''Drop Event Stage 1''
		Run When an object is dragged into the widget
		(with mouse button still being held)

		This Method determines if the dropEvent should be
		accepted or not.
		"""
		print event.mimeData().formats()			## List of All possible Mimetypes attached to data

		if event.mimeData().hasFormat('application/x-interpipeline'):
			event.accept()


	def dragMoveEvent( self, event ):
		"""
		''Drop Event Stage 2''
		Run When an object is moved within the widget
		(with mouse button still being held)

		IMPORTANT:
		Without this method, dropEvent is not called. (weird, right?)
		"""
		pass

	def dropEvent(self, event):
		"""
		''Drop Event Stage 3''
		Run if mouse is released over widget, 
		and event.accept() is set in dragEnterEvent(). 
		"""
		print event.source()		## The object being dragged


		if event.mimeData().hasFormat('application/x-interpipeline'):
		 	data = event.mimeData().data('application/x-interpipeline')
			print data

Drag/Drop (views)

https://doc.qt.io/archives/4.6/model-view-dnd.html

https://doc.qt.io/qt-5/model-view-programming.html#using-drag-and-drop-with-item-views