Issue
I have a python qt5 program with a QTreeWidget. Each QTreeWidgetItem represents files. I have the logic working correctly within mouseMoveEvent so that QDrag CopyAction passes the file mime data successfully to windows when the user drags it to windows explorer or the desktop. However, I need to know what that target path is. The QDrag target() function only returns a widget if the user drags within the application. If the user drags outside the application (like the desktop), then target() returns None.
Is there anyway to know the path the user dragged to in windows?
The reason I need to know the target path is because the file will actually be generated at runtime and the generation can take as long as a minute to complete. In this mvce example I am using touch command to instantly create an empty file so the user doesn't have to wait or see a long delay. Then after the actual file is generated a minute later, I want to replace the empty file with the actual file. My application can then tell the user that the file is now available. I can not pre-generate the file because there will be many in the list and each will take minutes to generate.
Here is my mvce (minimum reproducible example):
example.py
#!python3
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QTreeWidgetItem
from main_ui import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent) # initialization of the superclass
self.setupUi(self) # setup the GUI --> function generated by pyuic4
self.tree1.blockSignals(True)
self.tree1.clear()
self.tree1.setHeaderHidden(True)
self.tree1.setColumnCount(2)
self.tree1.setRootIsDecorated(True) # show expansion icon
level1 = QTreeWidgetItem(self.tree1)
level1.setText(0, "root")
level1.setExpanded(True)
level2 = QTreeWidgetItem(level1)
level2.setExpanded(False)
level2.setText(0, "dragme to desktop")
level2.setExpanded(True)
self.tree1.show()
self.tree1.resizeColumnToContents(0)
self.tree1.blockSignals(False)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
myapp = MainWindow() # instantiate the main window
rc = app.exec_()
sys.exit(rc) # exit with the same return code of Qt application
main.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>415</width>
<height>451</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="mytree" name="tree1">
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="autoScrollMargin">
<number>16</number>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>false</bool>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="columnCount">
<number>0</number>
</property>
<attribute name="headerVisible">
<bool>true</bool>
</attribute>
<attribute name="headerCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="headerDefaultSectionSize">
<number>100</number>
</attribute>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>mytree</class>
<extends>QTreeWidget</extends>
<header>mytree.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
mytree.py
#!python3
import tempfile
from pathlib import Path
from PyQt5 import QtGui
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
class mytree(QtWidgets.QTreeWidget): # mytree is now a subclass of qlineedit class
def __init__(self, parent=None):
super(mytree, self).__init__(parent) # The use of "super" is a slightly better method of calling the parent for initialization
self.setDragEnabled(True)
def mouseMoveEvent(self, event): # is called whenever the mouse moves while a mouse button is held down
super(mytree, self).mouseMoveEvent(event) #propagate
tempdir = tempfile.gettempdir() # temp directory aka %temp%
realfile = tempdir + "/empty.txt"
Path(realfile).touch() # create empty file
fileurl = "file:///%s" % realfile # build url
url = QtCore.QUrl(fileurl)
urllist = []
urllist.append(url) # only 1 file
drag = QtGui.QDrag(self)
md = QtCore.QMimeData()
md.setUrls(urllist)
drag.setMimeData(md)
result = drag.exec_(Qt.CopyAction)
source = drag.source()
print("source = %s" % source)
target = drag.target() # returns widget if inside the application, returns None if windows desktop ... etc.
print("target = %s" % target)
Solution
Cause
QDrag::target
returns a pointer to QObject
. Within a Qt widgets application this makes sense, because all possible drop targets are some kind of QWidget
s, i.e. QObject
s. When the drop action is outside the app, it does not make sense to return anything than a null pointer, because:
If not, a pointer to which
QObject
shouldtarget
return instead?If you somehow manage to get some kind of a pointer, to which type would you cast it to access the information you need, because QObject definitely does not contain any file URLs?
I admit the documentation of QDrag::target
is not as explicit on this, as the documentation of QDropEvent::source
:
Returns the target of the drag and drop operation. This is the widget where the drag object was dropped.
compared to
If the source of the drag operation is a widget in this application, this function returns that source; otherwise it returns nullptr.
However, the This is the widget part gives a hint about this fact, that the target should be a widget.
Solution
Instead of a drag functionality use QFileDialog::getSaveFileUrl
to get the url of the file.
Answered By - scopchanov
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.