PyQT4: Drag and drop files into QListWidget - python

I've been coding a OCR book scanning thing (it renames pages by reading the page number), and have switched to a GUI from my basic CLI Python script.
I'm using PyQT4 and looked at a ton of documents on drag and drop, but no luck. It just refuses to take those files! I was using these to articles for my UI design:
http://tech.xster.net/tips/pyqt-drag-images-into-list-widget-for-thumbnail-list/
http://zetcode.com/tutorials/pyqt4/dragdrop/
I noticed that there are a TON of ways to setup a PyQT4 GUI. Which one works the best?
Oops, here's the source code for the project.
The main script:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4.QtGui import QListWidget
from layout import Ui_window
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_window()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.listWidget, QtCore.SIGNAL("dropped"), self.picture_dropped)
def picture_dropped(self, l):
for url in l:
if os.path.exists(url):
picture = Image.open(url)
picture.thumbnail((72, 72), Image.ANTIALIAS)
icon = QIcon(QPixmap.fromImage(ImageQt.ImageQt(picture)))
item = QListWidgetItem(os.path.basename(url)[:20] + "...", self.pictureListWidget)
item.setStatusTip(url)
item.setIcon(icon)
class DragDropListWidget(QListWidget):
def __init__(self, type, parent = None):
super(DragDropListWidget, self).__init__(parent)
self.setAcceptDrops(True)
self.setIconSize(QSize(72, 72))
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
l = []
for url in event.mimeData().urls():
l.append(str(url.toLocalFile()))
self.emit(SIGNAL("dropped"), l)
else:
event.ignore()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
And the UI file...
# Form implementation generated from reading ui file 'layout.ui'
#
# Created: Thu Nov 11 00:22:52 2010
# by: PyQt4 UI code generator 4.8.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_window(object):
def setupUi(self, window):
window.setObjectName(_fromUtf8("window"))
window.resize(543, 402)
window.setAcceptDrops(True)
self.centralwidget = QtGui.QWidget(window)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.listWidget = QtGui.QListWidget(self.centralwidget)
self.listWidget.setProperty(_fromUtf8("cursor"), QtCore.Qt.SizeHorCursor)
self.listWidget.setAcceptDrops(True)
self.listWidget.setObjectName(_fromUtf8("listWidget"))
self.verticalLayout.addWidget(self.listWidget)
window.setCentralWidget(self.centralwidget)
self.retranslateUi(window)
QtCore.QMetaObject.connectSlotsByName(window)
def retranslateUi(self, window):
window.setWindowTitle(QtGui.QApplication.translate("window", "PyNamer OCR", None, QtGui.QApplication.UnicodeUTF8))
Thanks to anybody who can help!

The code you're using as an example seem to work fine and looks quite clean. According to your comment your list widget is not getting initialized; this should be the root cause of your issue. I've simplified your code a bit a tried it on my Ubuntu 10.04LTS and it worked fine. My code is listed below, see if it would for you also. You should be able to drag and drop a file into the list widget; once it's dropped a new item is added showing the image and image's file name.
import sys
import os
from PyQt4 import QtGui, QtCore
class TestListView(QtGui.QListWidget):
def __init__(self, type, parent=None):
super(TestListView, self).__init__(parent)
self.setAcceptDrops(True)
self.setIconSize(QtCore.QSize(72, 72))
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.emit(QtCore.SIGNAL("dropped"), links)
else:
event.ignore()
class MainForm(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
self.view = TestListView(self)
self.connect(self.view, QtCore.SIGNAL("dropped"), self.pictureDropped)
self.setCentralWidget(self.view)
def pictureDropped(self, l):
for url in l:
if os.path.exists(url):
print(url)
icon = QtGui.QIcon(url)
pixmap = icon.pixmap(72, 72)
icon = QtGui.QIcon(pixmap)
item = QtGui.QListWidgetItem(url, self.view)
item.setIcon(icon)
item.setStatusTip(url)
def main():
app = QtGui.QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
main()
hope this helps, regards

Related

How to enable button after QListWidget is no longer empty

I would like my button to be disabled until there are items actually in my QListWidget. I have tried if and while statements in my AppDemo class but I feel like it needs to change in the ListBoxWidget class, however the listBoxWidget is already a sub class of the AppDemo.
Example Code:
import sys, os
from PyQt5.QtWidgets import QApplication, QMainWindow, QListWidget, QListWidgetItem, QPushButton
from PyQt5.QtCore import Qt, QUrl
class ListBoxWidget(QListWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setAcceptDrops(True)
self.resize(600, 600)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
if url.isLocalFile():
links.append(str(url.toLocalFile()))
else:
links.append(str(url.toString()))
self.addItems(links)
else:
event.ignore()
class AppDemo(QMainWindow):
def __init__(self):
super().__init__()
self.resize(1200, 600)
self.listbox_view = ListBoxWidget(self)
self.btn = QPushButton('Get Value', self)
self.btn.setEnabled(False)
self.btn.setGeometry(850, 400, 200, 50)
self.btn.clicked.connect(lambda: print(self.getSelectedItem()))
def getSelectedItem(self):
item = QListWidgetItem(self.listbox_view.currentItem())
return item.text()
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = AppDemo()
demo.show()
sys.exit(app.exec_())
You have to use the signals that are emitted when the number of rows of the model associated with the view changes, those signals must invoke a method that updates the state of the button based on the number of items in the QListWidget:
class AppDemo(QMainWindow):
def __init__(self):
super().__init__()
self.resize(1200, 600)
self.listbox_view = ListBoxWidget(self)
self.listbox_view.model().modelReset.connect(self.handle_rows_changed)
self.listbox_view.model().rowsInserted.connect(self.handle_rows_changed)
self.listbox_view.model().rowsRemoved.connect(self.handle_rows_changed)
self.listbox_view.model().layoutChanged.connect(self.handle_rows_changed)
self.btn = QPushButton("Get Value", self)
self.btn.setEnabled(False)
self.btn.setGeometry(850, 400, 200, 50)
self.btn.clicked.connect(lambda: print(self.getSelectedItem()))
def getSelectedItem(self):
item = self.listbox_view.currentItem()
return item.text() if item is not None else ""
def handle_rows_changed(self):
self.btn.setEnabled(bool(self.listbox_view.count()))

How does QGraphicsView receive mouse move events?

Briefly I want to track mouse coordinate over a QGraphicsView.
This answer works well for a QLabel object, but not works as expected when I switch to QGraphicsView as follows:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.graphicsView = QtGui.QGraphicsView(self)
self.graphicsView.setMouseTracking(True)
self.graphicsView.installEventFilter(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.graphicsView)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.MouseMove and
source is self.graphicsView):
pos = event.pos()
print('mouse move: (%d, %d)' % (pos.x(), pos.y()))
return QtGui.QWidget.eventFilter(self, source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
window.resize(200, 100)
sys.exit(app.exec_())
Specifically, it seems like that the event is caught only when my cursor moves accross the border of the QGraphicsView (the black lines).
Could anyone tell me why and is there any better solutions?
For certain widgets, you need to use its viewport instead:
self.graphicsView.viewport().installEventFilter(self)
...
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.MouseMove and
source is self.graphicsView.viewport()):
...
An alternative is to override mouseMoveEvent(event) of QGraphicsView directly.
Example:
from PySide import QtGui
class MyGraphicsView(QtGui.QGraphicsView):
def __init__(self, parent):
QtGui.QGraphicsView.__init__(self, parent)
self.setMouseTracking(True)
def mouseMoveEvent(self, event):
print('mouseMoveEvent: pos {}'.format(event.pos()))
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.graphicsView = MyGraphicsView(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.graphicsView)
app = QtGui.QApplication([])
window = Window()
window.show()
window.resize(200, 100)
app.exec_()

Drag and Drop in pyqt4 with modifier keys

Consider the following minimal example. How can I modify it, that it does something else, when a modifier key is pressed during the drop event as indicated in the comment in the code snipped below. Suppose you drop a file from nautilus or something like that to my example.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MyDropLabel(QLabel):
myDroppedSignal = pyqtSignal(str)
myDroppedSignalModified = pyqtSignal()
def __init__(self,text):
super(MyDropLabel, self).__init__(text)
self.setAcceptDrops(True)
def dragMoveEvent(self, e):
e.accept()
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
if e.mimeData().hasUrls():
s = e.mimeData().urls()[0].path()
self.myDroppedSignal.emit(s)
e.accept()
##if a modifier key, for example shift or ctrl was pressed to something else and emit myDroppedSignalModified
class MyWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.hlayout = QHBoxLayout()
self.setLayout(self.hlayout)
self.label = MyDropLabel("Drop something here")
self.hlayout.addWidget(self.label)
#self.setCentralWidget(self.label)
self.label.myDroppedSignal.connect(self.myDroppedHandler)
def myDroppedHandler(self,s):
self.label.setText(QString(s))
if __name__ == "__main__":
app = QApplication(sys.argv)
da = MyWidget()
da.show()
sys.exit(app.exec_())
You can, for example, set a self.keyIsPressed state by installEventFilter( QObject * filterObj ) to filter the QEvent.KeyPress that you define.
This is an example using QApplication.keyboardModifiers:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import os
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class droppableLabel(QLabel):
fileDropped = pyqtSignal(list)
def __init__(self, type, parent=None):
super(droppableLabel, self).__init__(parent)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.fileDropped.emit(links)
else:
event.ignore()
class droppableWidget(QWidget):
def __init__(self, parent=None):
super(droppableWidget, self).__init__(parent)
self.label = droppableLabel(self)
self.label.fileDropped.connect(self.on_label_fileDropped)
self.label.setText("Press CTRL/SHIFT/None and drop some files here")
self.label.setMinimumSize(QSize(40, 100))
self.verticalLayout = QVBoxLayout(self)
self.verticalLayout.addWidget(self.label)
self.verticalLayout.setMargin(0)
#pyqtSlot(list)
def on_label_fileDropped(self, fileNames):
droppedFiles = [ fileName
for fileName in fileNames
if os.path.exists(fileName)
]
if droppedFiles:
keyModifiers = QApplication.keyboardModifiers()
if keyModifiers == Qt.ShiftModifier:
print "SHIFT"
formatter = "\n"
elif keyModifiers == Qt.ControlModifier:
print "CTRL"
formatter = ","
else:
print "NONE"
formatter = "|"
self.label.setText(formatter.join(droppedFiles))
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
main = droppableWidget()
main.show()
sys.exit(app.exec_())

PyQt how to get sender (widget) in closeEvent?

I'm trying to catch a closeEvent for several dockWidgets that get added dynamically to a QMainWindow. It is unclear to me how I can figure out which widget has been closed.. Here's an simplified example:
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.leftDockWidget = QtGui.QDockWidget('pick tool', self)
self.leftDockWidget.setWidget( QtGui.QLabel('a dock widget') )
self.addDockWidget( QtCore.Qt.LeftDockWidgetArea, self.leftDockWidget )
self.leftDockWidget.closeEvent = self.dockWidgetCloseEvent
self.show()
def dockWidgetCloseEvent(self, event):
print event
# how to get sender widget ?
event.sender() doesn't seem to exist..
any ideas ?
thanks
One way to achieve what you want would be to use an event filter:
from PyQt4 import QtGui, QtCore
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.leftDockWidget = QtGui.QDockWidget('pick tool', self)
self.leftDockWidget.setWidget(QtGui.QLabel('a dock widget'))
self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.leftDockWidget)
self.leftDockWidget.installEventFilter(self)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.Close and
isinstance(source, QtGui.QDockWidget)):
print source.windowTitle()
return super(Example, self).eventFilter(source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Example()
window.show()
sys.exit(app.exec_())
def dockWidgetCloseEvent(self, event):
self.sender()

Phonon videowidget on qgraphicsview

I would like to place a phonon videowidget onto a qgraphicsscene so I can overlay graphics etc. When I run the following I get the sound, but no video playing on the qgraphicsview. Help would be appreciated as I thought I was doing as the examples show. However, I suspect its something in how I have understood videoplayer and videowidget.
For testing I am just playing a video straight from a file.
from PySide import QtGui, QtCore
from PySide.phonon import Phonon
from window import Ui_MainWindow # main GUI window
import os, sys
class DiagramScene(QtGui.QGraphicsScene):
InsertItem, InsertLine, InsertText, MoveItem = range(4)
def __init__(self):
super(DiagramScene, self).__init__()
self.myLineColor = QtCore.Qt.black
self.myMode = "Start"
self.line = None
def mousePressEvent(self, mouseEvent):
if (mouseEvent.button() == QtCore.Qt.LeftButton):
if self.myMode == "Start":
self.line = QtGui.QGraphicsLineItem(QtCore.QLineF(mouseEvent.scenePos(), mouseEvent.scenePos()))
self.addItem(self.line)
elif (mouseEvent.button() == QtCore.Qt.RightButton):
self.addText("Hello")
super(DiagramScene, self).mousePressEvent(mouseEvent)
def mouseMoveEvent(self, mouseEvent):
if self.line:
newLine = QtCore.QLineF(self.line.line().p1(), mouseEvent.scenePos())
self.line.setLine(newLine)
def mouseReleaseEvent(self, mouseEvent):
self.line = None
super(DiagramScene, self).mouseReleaseEvent(mouseEvent)
class QPlayer(QtGui.QWidget):
def __init__(self):
super(QPlayer, self).__init__()
media_src = Phonon.MediaSource("C:\Users\Public\Videos\Sample Videos\Wildlife.wmv")
self.audioOuptut=Phonon.AudioOutput(Phonon.MusicCategory, self)
self.player=Phonon.MediaObject(self)
self.player.setCurrentSource(media_src)
Phonon.createPath(self.player, self.audioOuptut)
self.videoWidget=Phonon.VideoWidget(self)
self.videoWidget.FitInView
Phonon.createPath(self.player, self.videoWidget)
self.player.play()
class Main(QtGui.QMainWindow):
def __init__(self):
super(Main, self).__init__()
self.ui=Ui_MainWindow()
self.ui.setupUi(self)
self.scene = DiagramScene()
self.scene.addWidget(QPlayer())
self.gview = self.ui.gView
self.gview.setScene(self.scene)
def main():
app = QtGui.QApplication(sys.argv)
window=Main()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Ok - think I've sorted it (to an extent). Simple case of:
self.videoWidget.setMinimumSize(640,480)
The video doesn't really run very well - breaks up a lot but at least I can draw on it :)

Categories

Resources