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().
Related
Consider the following code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class First(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
x = Button(text='somebutton')
x.bind(on_press=lambda*_: print('First press'))
x.bind(on_press=lambda*_: print('Second press'))
self.add_widget(x)
def something(self, *somethingishereignored):
print("I have something")
class FChooser(App):
def build(self):
return First()
if __name__ == '__main__':
FChooser().run()
The behaviour of this code is that, after I press the 'somebutton' button, it prints:
Second press
First press
So, I googled and found that I should use the unbind() function, and I added this:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class First(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
x = Button(text='somebutton')
x.bind(on_press=lambda*_: print('First press'))
x.unbind(on_press=lambda*_: print('First press'))
x.bind(on_press=lambda*_: print('Second press'))
self.add_widget(x)
def something(self, *somethingishereignored):
print("I have something")
class FChooser(App):
def build(self):
return First()
if __name__ == '__main__':
FChooser().run()
but the output doesn't change. It's still the same output. How do I release the bind? This is just a minimal example, and I intend to use this functionality to dynamically bind and unbind a function to a button, to add various functionality to the same button.
The function will not unbind, because you do not refer to the function you bound to. As you use inline lambda, that method reference was not saved, so you can't use it to unbind later.
This will work tho:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class First(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
x = Button(text='somebutton')
x.bind(on_press=self.press1)
x.unbind(on_press=self.press1)
x.bind(on_press=self.press2)
self.add_widget(x)
def press1(self, *args):
print("First press")
def press2(self, *args):
print("Second press")
class FChooser(App):
def build(self):
return First()
if __name__ == '__main__':
FChooser().run()
Or this:
press1 = lambda*_: print('First press')
press2 = lambda*_: print('Second press')
x = Button(text='somebutton')
x.bind(on_press=press1)
x.unbind(on_press=press1)
x.bind(on_press=press2)
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'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())
If I have an open QWebView, I like its default context menu with "Open in New Window" as an option for links. However, I can't seem to find a way to act when the user requests a link be opened in a new window. Overriding the QWebPage.createWindow method doesn't seem to work, because the method is not invoked when the user chooses to open a link in a new window.
Any recommendations? I'm using PyQt.
Example code:
class LocalWebPage(QWebPage):
def acceptNavigationRequest(self, webFrame, networkRequest, navigationType):
print '*acceptNavigationRequest**',webFrame, networkRequest, navigationType
return QWebPage.acceptNavigationRequest(self, webFrame, networkRequest, navigationType)
def createWindow(self, windowType):
print '--createWindow', windowType
return QWebPage.createWindow(self, windowType)
class Browser(Ui_MainWindow, QMainWindow):
def __init__(self, base, name):
...
self.page = LocalWebPage()
self.webViewMain = QWebView(self.centralwidget)
self.webViewMain.setPage(self.page)
...
I have the debugging prints in there to verify that createWindow is not being called.
You'll need to call the createWindow method of the QWebView yourself, for example by reimplementing the triggerAction of the QWebPage, something like this:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtGui, QtCore, QtWebKit
class MyPage(QtWebKit.QWebPage):
def __init__(self, parent=None):
super(MyPage, self).__init__(parent)
def triggerAction(self, action, checked=False):
if action == QtWebKit.QWebPage.OpenLinkInNewWindow:
self.createWindow(QtWebKit.QWebPage.WebBrowserWindow)
return super(MyPage, self).triggerAction(action, checked)
class MyWindow(QtWebKit.QWebView):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.myPage = MyPage(self)
self.setPage(self.myPage)
def createWindow(self, windowType):
if windowType == QtWebKit.QWebPage.WebBrowserWindow:
self.webView = MyWindow()
self.webView.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
return self.webView
return super(MyWindow, self).createWindow(windowType)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.show()
main.load(QtCore.QUrl("http://www.example.com"))
sys.exit(app.exec_())
The link that was right-clicked can be found by using hitTestContent in the contextMenuEvent method of the QWebView:
def contextMenuEvent(self, event):
pos = event.pos()
element = self.page().mainFrame().hitTestContent(pos)
link_url = str(element.linkUrl().toString())
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)