I'm trying to display files and folders like column view in Mac finder.
I was able get the basic structure with the help of ListViews and QFileSystemModel. Then I set the splitter handle as a corner widget for the scroll area. I have two issues here
When I resize the listview, the splitter handle disappears.
Even after setting the splitter handle width to 0, I see spacing between
listviews.
# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui
import os
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class PopulateList(QtGui.QDialog):
def __init__(self,parent=None):
super().__init__(parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
self.setModel()
self.show()
self.ui.splitter.setHandleWidth(0)#not working
self.ui.listView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.ui.listView.setCornerWidget(self.getCornerWidget(self.ui.splitter))
self.ui.closePushButton.clicked.connect(self.close)
self.ui.listView.clicked.connect(self.showSubFiles)
def getCornerWidget(self, splitter):
self.handle=splitter.handle(1)
layout=QtGui.QHBoxLayout(self.handle)
layout.setSpacing(0)
layout.setMargin(0)
for i in range(0,2):
line = QtGui.QFrame(self.handle)
line.setFrameShape(QtGui.QFrame.VLine)
layout.addWidget(line)
return self.handle
def showSubFiles(self, index):
root_path = self.model.fileInfo(index).absoluteFilePath()
self.model1=QtGui.QFileSystemModel()
self.model1.setRootPath(root_path)
self.ui.listView_1.setModel(self.model1)
self.ui.listView_1.setRootIndex(self.model1.index(root_path))
def setModel(self):
root_path=os.path.expanduser("~")
self.model=QtGui.QFileSystemModel()
self.model.setRootPath(root_path)
self.model.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Dirs)
self.ui.listView.setModel(self.model)
self.ui.listView.setRootIndex(self.model.index(root_path))
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName(_fromUtf8("Form"))
Form.resize(602, 365)
self.verticalLayout = QtGui.QVBoxLayout(Form)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.splitter = QtGui.QSplitter(Form)
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.splitter.setObjectName(_fromUtf8("splitter"))
self.listView = QtGui.QListView(self.splitter)
self.listView.setObjectName(_fromUtf8("listView"))
self.listView_1 = QtGui.QListView(self.splitter)
self.listView_1.setObjectName(_fromUtf8("listView_1"))
self.verticalLayout.addWidget(self.splitter)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.closePushButton = QtGui.QPushButton(Form)
self.closePushButton.setObjectName(_fromUtf8("closePushButton"))
self.horizontalLayout.addWidget(self.closePushButton)
self.verticalLayout.addLayout(self.horizontalLayout)
self.verticalLayout.setStretch(0, 1)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(_translate("Form", "Form", None))
self.closePushButton.setText(_translate("Form", "Close", None))
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
listView=PopulateList()
sys.exit(app.exec_())
Are you able to use QColumnView? It is exactly this.
http://doc.qt.io/qt-5/qcolumnview.html
import sys
from PyQt5 import QtWidgets, QtCore
app = QtWidgets.QApplication(sys.argv)
model = QtWidgets.QFileSystemModel()
view = QtWidgets.QColumnView()
view.setModel(model)
model.setRootPath("/")
view.show()
sys.exit(app.exec())
Related
I'm trying to run a simple PyQt5 application on Linux, the code is as follows:
#!/usr/bin/python
import sys
from PyQt5.QtWidgets import QApplication, QWidget
def main():
app = QApplication(sys.argv)
w = QWidget()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
mime = app.clipboard().mimeData()
print(mime.hasImage()) # True
print(mime.imageData()) # None
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Before running it, I copied an image into the clipboard, so mime.hasImage() should return True. No problem, that's also the case. But what's weird is, mime.imageData() sometimes returns None. that shouldn't happen. mime.imageData() should contain the image that I copied instead of None. Is there anything wrong with the code?
By the way, this seems to only happen on Linux, mime.imageData() never returns None on Windows. I'm using python3
That hasImage() returns True does not imply that imageData() returns a QImage since it only indicates that the user copied an image to the clipboard, and in what format do I copy the image? Well, it could be png, jpg, etc or it could provide the url for the client application to download or html to insert it into the client application and then obtain the image by rendering the HTML.
So in general the application from which the image was copied is responsible for the sending format and that there is no restrictive standard for that format but there are common formats.
The following example shows the logic to handle the images that come from urls and HTML:
#!/usr/bin/python
import sys
from functools import cached_property
from PyQt5.QtCore import pyqtSignal, QObject, QUrl
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt5.QtGui import QGuiApplication, QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from bs4 import BeautifulSoup
class ImageDownloader(QObject):
finished = pyqtSignal(QImage)
def __init__(self, parent=None):
super().__init__(parent)
self.manager.finished.connect(self.handle_finished)
#cached_property
def manager(self):
return QNetworkAccessManager()
def start_download(self, url):
self.manager.get(QNetworkRequest(url))
def handle_finished(self, reply):
if reply.error() != QNetworkReply.NoError:
print("error: ", reply.errorString())
return
image = QImage()
image.loadFromData(reply.readAll())
self.finished.emit(image)
class ClipboardManager(QObject):
imageChanged = pyqtSignal(QImage)
def __init__(self, parent=None):
super().__init__(parent)
QGuiApplication.clipboard().dataChanged.connect(
self.handle_clipboard_datachanged
)
self.downloader.finished.connect(self.imageChanged)
#cached_property
def downloader(self):
return ImageDownloader()
def handle_clipboard_datachanged(self):
mime = QGuiApplication.clipboard().mimeData()
if mime.hasImage():
image = mime.imageData()
if image is not None:
self.imageChanged.emit(image)
elif mime.hasUrls():
url = mime.urls()[0]
self.downloader.start_download(urls[0])
elif mime.hasHtml():
html = mime.html()
soup = BeautifulSoup(html, features="lxml")
imgs = soup.findAll("img")
if imgs:
url = QUrl.fromUserInput(imgs[0]["src"])
self.downloader.start_download(url)
else:
for fmt in mime.formats():
print(fmt, mime.data(fmt))
def main():
app = QApplication(sys.argv)
label = QLabel(scaledContents=True)
label.resize(250, 150)
label.move(300, 300)
label.setWindowTitle("Simple")
label.show()
manager = ClipboardManager()
manager.imageChanged.connect(
lambda image: label.setPixmap(QPixmap.fromImage(image))
)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
this is code with player only work fine ; with treeview only also work but together the app crash immediately if i click on radio button to show the treeview.
But if i click open button and the file explorer appear than it work and i can use radiobutton without problem ; i don't know why.
import sys
from PyQt5 import QtGui, QtWidgets, QtCore, uic
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtMultimedia import *
from PyQt5.QtMultimediaWidgets import *
from PyQt5.uic import loadUi
class MyApp(QMainWindow):
def __init__(self):
super(MyApp, self).__init__()
loadUi("window.ui", self)
#########################################################################
self.model = QFileSystemModel(self)
self.model.setRootPath(self.model.myComputer())
self.treeview.setModel(self.model)
self.treeview.clicked.connect(self.on_treeView_clicked)
self.folder_open.toggled.connect(self.HideShow) # show/hide treeview
#########################################################################
self.media = QMediaPlayer()
video = QVideoWidget(self.videoShow)
video.setGeometry(QtCore.QRect(0, 0, 439, 282))
self.playButton.clicked.connect(self.playVideo)
self.openbtn.clicked.connect(self.openFile)
self.media.setVideoOutput(video)
#########################################################################
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_treeView_clicked(self, index):
indexItem = self.model.index(index.row(), 0, index.parent())
filePath = self.model.filePath(indexItem)
self.choice.setText(filePath)
def HideShow(self):
if self.folder_open.isChecked() == True:
self.showTree()
if self.folder_open.isChecked() == False:
self.hideTree()
def showTree(self):
self.list.setFixedSize(443, 310)
window.setFixedSize(1200, 320)
def hideTree(self):
self.list.setFixedSize(0, 310)
window.setFixedSize(750, 320)
###########################################################################
def openFile(self):
fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie",QDir.homePath())
if fileName != '':
self.media.setMedia(QMediaContent(QUrl.fromLocalFile(fileName)))
def playVideo(self):
fileName = self.choice.text()
self.media.setMedia(QMediaContent(QUrl.fromLocalFile(fileName)))
self.media.play()
#if self.media.state() == QMediaPlayer.PlayingState:
#self.media.pause()
#else:
#self.media.play()
###########################################################################
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.setFixedSize(750, 320)
window.show()
sys.exit(app.exec_())
window.ui used : download here
I have this code:
import sys, random, pprint
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5 import QtGui
from PyQt5.QtCore import Qt
class Window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(QtCore.QRect(200, 200, 700, 700))
self.widget = QtWidgets.QWidget(self)
self.widget.setGeometry(QtCore.QRect(10, 10, 400, 200))
self.widget.setObjectName("widget")
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self.widget)
self.drawPoints(painter)
painter.end()
def drawPoints(self, painter):
#drawing code
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Window()
ex.show()
sys.exit(app.exec_())
What do i expect: area (400x200) which would be painted by drawPoints(). QWidget inherits QPaintDevice. So this code should work. But console says:
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::end: Painter not active, aborted
Could you help me, please.
It looks like you are attempting to replicate this example, correct?
The major difference appears to be that you are defining paintEvent() for the main window, rather than the widget you are actually trying to paint. It would work if you created a custom subclass of QWidget, defined paintEvent() there, and then inserted that into a standard QMainWindow instance, like this:
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent=parent)
self.initUI()
def initUI(self):
self.setGeometry(QtCore.QRect(10, 10, 400, 200))
self.setObjectName("widget")
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
self.drawPoints(painter)
painter.end()
def drawPoints(self, painter):
pass
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
my_window = QtWidgets.QMainWindow()
my_window.setGeometry(QtCore.QRect(200, 200, 700, 700))
my_window.setCentralWidget(MyWidget(my_window))
my_window.show()
sys.exit(app.exec_())
At the very least, it isn't printing that error message anymore.
I have this small QT program:
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import sys
class QtZListView(QtGui.QListView):
def __init__(self, *args, **kwargs):
QtGui.QListView.__init__(self, *args, **kwargs)
self.model = QtGui.QStringListModel(['a','b','c'])
self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.setModel(self.model)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setDragEnabled(True)
def setStringList(self, *args, **kwargs):
return self.model.setStringList(*args, **kwargs)
class mplsubwindow(QtGui.QMdiSubWindow):
def __init__(self, *args, **kwargs):
QtGui.QMdiSubWindow.__init__(self, *args, **kwargs)
self.setWindowTitle("testing")
self.setAcceptDrops(True)
fig = Figure(figsize=(5, 4), dpi=100,
facecolor = self.palette().color(QtGui.QPalette.Background).name()
)
p = FigureCanvas(fig)
self.axes = fig.add_subplot(111)
self.axes.hold(False)
FigureCanvas.setSizePolicy(
self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding
)
FigureCanvas.updateGeometry(self)
fig.tight_layout()
toolbar = NavigationToolbar(p, self)
self.layout().addWidget(toolbar)
self.layout().addWidget(p)
self.resize(400,400)
self.show()
def dragEnterEvent(self, event):
print('entering')
super(mplsubwindow, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
print('drag moving')
super(mplsubwindow, self).dragMoveEvent(event)
def dropEvent(self, event):
print('dropped')
super(mplsubwindow, self).dropEvent(event)
class ExampleApp(QtGui.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
mainwid = QtGui.QWidget()
layout = QtGui.QGridLayout()
mainwid.setLayout(layout)
self.mdiarea = QtGui.QMdiArea()
self.setCentralWidget(mainwid)
layout.addWidget(self.mdiarea)
sub = mplsubwindow(self.mdiarea)
fig = Figure()
p = FigureCanvas(fig)
sub.layout().addWidget(p)
sub.show()
layout.addWidget(QtZListView())
def main():
app = QtGui.QApplication(sys.argv)
form = ExampleApp()
form.show()
app.exec_()
if __name__ == '__main__':
main()
I want to be able to drag one or more items from the list in the bottom into the matplotlib canvas. For some reason only the enter-event is invoked...the remaining drag/drop events seems to be ignored...and furthermore it seems like the QMdiSubWindow does not accept drops even though i set setAcceptDrops(True).
What am I missing here?
You need to accept the event in the dragEnterEvent method or else move and drop events are ignored.
def dragEnterEvent(self, event):
print('entering')
event.accept()
super(mplsubwindow, self).dragEnterEvent(event)
Note that I really want to emphasise you should be adding the widget via QMdiSubWindow.setWidget(). Any of the other methods (like using QMdiSubWindow.layout() or QMdiSubWindow.setCentralWidget()) are not fully supported by MDI windows and will likely lead to other issues down the road. If you feel that setWidget() is not doing what you want, ask a new question detailing the issue so that it can be resolved while still using setWidget().
I created a widget in qt designer and transformed the ui file using pyuic to a python class called Ui_wid_canvas. This is supposed to be used as special canvas:
# file mgcanvas.py
from PyQt4 import QtCore, QtGui
class Ui_wid_canvas(object):
def setupUi(self, wid_canvas):
wid_canvas.setObjectName("wid_canvas")
wid_canvas.resize(400, 300)
self.horizontalLayout = QtGui.QHBoxLayout(wid_canvas)
self.horizontalLayout.setObjectName("horizontalLayout")
self.pushButton = QtGui.QPushButton(wid_canvas)
self.pushButton.setObjectName("pushButton")
self.horizontalLayout.addWidget(self.pushButton)
self.retranslateUi(wid_canvas)
QtCore.QMetaObject.connectSlotsByName(wid_canvas)
def retranslateUi(self, wid_canvas):
wid_canvas.setWindowTitle(QtGui.QApplication.translate("wid_canvas", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton.setText(QtGui.QApplication.translate("wid_canvas", "PushButton", None, QtGui.QApplication.UnicodeUTF8))
From Ui_wid_canvas I derive a class MyCanvas to implement the paintEvent function and some utility functions such as moo(). Within the paintevent all it shall do is draw two rects. If I use the following class as my application everything works like a charm.
# file mycanvas.py
from PyQt4 import QtCore, QtGui
import mgcanvas
class MyCanvas(mgcanvas.Ui_wid_canvas, QtGui.QWidget):
def __init__(self):
super(mgcanvas.Ui_wid_canvas, self).__init__()
self.setupUi(self)
def paintEvent(self, qpaintevent):
print "PaintEvent canvas"
painter = QtGui.QPainter(self)
painter.setBrush(QtGui.QColor(255,0,0,80))
painter.setPen(QtGui.QColor(00,00,00,255))
painter.drawRect(10,10,100,100)
r = QtCore.QRectF(110,110,100,100)
painter.drawRect(r)
painter.drawText(r,"Hello", QtGui.QTextOption(QtCore.Qt.AlignCenter))
def moo(self):
print "This is canvas mooing"
Now, when I create an application Test instantiating MyCanvas (see below), the paintEvent for Test is called, but the paintevent for MyCanvcas is never called, the rects are not drawn and no output "Paintevent Canvas" on the console. If I call self.widget.update() or self.widget.redraw() in Test.paintevent() the paintevent is not caught. If I call self.widget.paintevent() manually, the function is called, but the painter not activated. The pushbutton, on the other hand, is shown from which I figure that the widget is included correctly, but just not the paint event is called by the child widget.
# file test.py; executed with `python test.py`
from PyQt4 import QtCore, QtGui
import mycanvas
class Test(object):
def setupUi(self, Gui):
self.counter = 0
Gui.setObjectName("TestObject")
Gui.resize(500,500)
self.layout = QtGui.QVBoxLayout()
self.widget = mycanvas.MyCanvas()
self.widget.setupUi(self)
self.widget.setObjectName("wid_canvas")
self.layout.addWidget(self.widget)
self.retranslateUi(Gui)
QtCore.QMetaObject.connectSlotsByName(Gui)
def retranslateUi(self, Gui):
Gui.setWindowTitle(QtGui.QApplication.translate("TestObject", "Title", None, QtGui.QApplication.UnicodeUTF8))
def paintEvent(self, qpaintevent):
print "---> Enter"
self.counter += 1
print "counter", self.counter
self.widget.repaint()
self.widget.moo()
print "<-- Leave"
class MyTest(Test, QtGui.QWidget):
def __init__(self):
super(Test, self).__init__()
self.setupUi(self)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
ui = MyTest()
ui.show()
sys.exit(app.exec_())
Setting the Qt.WA_PaintOutsidePaintEvent is not an option, because it does not work on Mac and Windows but I'd like to stay platform independent.
Please excuse me posting so much code, but I guess it will make things easier. I tried to keep it to a minimum. Can someone tell me how I can have the Widget MyCanvas paint on itself and include this painting widget in another widget MyTest, which will work as the application?
In you class Test, you didn't attach the layout to the parameter Gui, by passing it as parameter to QVBoxLayout, and you called the self.widget.setupUi for MyCanvas although it was already called by MyCanvas constructor.
class Test(object):
def setupUi(self, Gui):
self.counter = 0
Gui.setObjectName("TestObject")
Gui.resize(500,500)
self.layout = QtGui.QVBoxLayout(Gui)
self.widget = mycanvas.MyCanvas()
self.widget.setObjectName("wid_canvas")
self.layout.addWidget(self.widget)
self.retranslateUi(Gui)
QtCore.QMetaObject.connectSlotsByName(Gui)