Make QToolBar Items have ContextMenu - python

How can I add a context menu to the items in my QToolBar. The reason for the context menu, is i want to give each Action a right-click > Delete action. Similar to the Chrome browsers ability to remove bookmarks from the bookmark toolbar. How do i achieve this in Pyside?
import os, sys
from PySide import QtGui, QtCore
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.resize(300,200)
self.createToolBars()
self.createActions()
def createToolBars(self):
self.toolBar = self.addToolBar('Presets')
self.toolBar.setIconSize(QtCore.QSize(16,16))
self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
def createActions(self):
self.toolBar.clear()
presets = ['Abby','Kevin','Bob','Mary']
for x in presets:
act = QtGui.QAction(x, self)
act.setData(x)
self.toolBar.addAction(act)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())

You have to implement a custom QToolBar and implement the contextMenuEvent method:
import os
import sys
from PySide import QtCore, QtGui
class ToolBar(QtGui.QToolBar):
def contextMenuEvent(self, event):
current_action = self.actionAt(event.pos())
if current_action is None:
return
menu = QtGui.QMenu()
delete_action = menu.addAction("delete")
action = menu.exec_(event.globalPos())
if action == delete_action:
self.removeAction(current_action)
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.resize(300,200)
self.createToolBars()
self.createActions()
def createToolBars(self):
self.tool_bar = ToolBar('Presets', self)
self.addToolBar(self.tool_bar)
self.tool_bar.setIconSize(QtCore.QSize(16,16))
self.tool_bar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
def createActions(self):
self.tool_bar.clear()
presets = ['Abby','Kevin','Bob','Mary']
for x in presets:
act = QtGui.QAction(x, self)
act.setData(x)
self.tool_bar.addAction(act)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())

Related

Include MenuBar from seperated file

I am stack trying to include MenuBar from separate file and trying to connect with function
I include some code that I have the same problem
foo.py
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUiType
import os
import sys
from foomenu import menu
FROM_MAIN, _ = loadUiType(os.path.join(os.path.dirname(__file__), "SalesGui.ui"))
class Main(QMainWindow, FROM_MAIN):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
self.MyMenu = menu(self)
self.MyMenu.NewProduct.triggered.connect(self.NewProduct())
def NewProduct(self):
print("foo")
def main():
app = QApplication(sys.argv)
window = Main()
window.show()
app.exec_()
if __name__ == '__main__':
try:
main()
except Exception as why:
print(why)
and foomenu.py
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QMenu
def menu(self):
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu('FooMenu')
NewProduct = QAction(QIcon('icons/exit.png'), 'Foo', self)
NewProduct.setShortcut('Ctrl+Q')
NewProduct.setStatusTip('FooAction')
fileMenu.addAction(NewProduct)
When Trying to connect the "NewProduct" button with "New Product" function I get the following error
'NoneType' object has no attribute 'NewProduct'
Try it:
import os
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QMenu
#from PyQt5.uic import loadUiType
#from foomenu import menu
#FROM_MAIN, _ = loadUiType(os.path.join(os.path.dirname(__file__), "SalesGui.ui"))
def menu(self):
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu('FooMenu')
self.NewProduct = QAction(QIcon('exit.png'), 'Foo', self) # 'icons/exit.png' # + self
self.NewProduct.setShortcut('Ctrl+Q')
self.NewProduct.setStatusTip('FooAction')
fileMenu.addAction(self.NewProduct)
return self.NewProduct # +++
class Main(QMainWindow):#, FROM_MAIN):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
# self.setupUi(self)
self.MyMenu = menu(self)
# self.MyMenu.NewProduct.triggered.connect(self.funcNewProduct) # - ()
self.MyMenu.triggered.connect(self.funcNewProduct) # +
def funcNewProduct(self):
print("foo")
qApp.quit()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Main()
window.show()
app.exec_()
It is much more elegant to subclass and setup the menubar in this another python file then import and instance this class in your file that holds the Main class.
In the MyMenu class you can binding the signal a socket locally that can provide from here the sender object for the parent class.
BTW you can iterate over the action object of the menubar/menus but it is much more harder to maintain and control then the explicit describe where to connect the potential different actions.
Menu.py:
class MyMenu(QMenuBar):
new_product_clicked = Signal(object)
def __init__(self, parent=None):
super(MyMenu, self).__init__(parent)
file_menu = QMenu("File menu", self)
new_product_action = QAction('Foo', self)
new_product_action.setShortcut('Ctrl+Q')
new_product_action.setStatusTip('FooAction')
new_product_action.triggered.connect(self.new_product_clicked)
file_menu.addAction(new_product_action)
self.addMenu(file_menu)
def new_product_clicked(self):
""" Call a method from the parent class. """
self.new_product_clicked.emit(self.sender())
Main.py:
from Menu import MyMenu
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
my_menu = MyMenu(self)
self.setMenuBar(my_menu)
self.my_menu.new_product_clicked.connect(self.product_clicked)
def product_clicked(self, action):
""" Socket for the clicked action """
clicked_action = action
print clicked_action.text()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Main()
window.show()
app.exec_()

How do I get a signal from a QWiget to change get object?

Hello I have a QWidget and If I click on it, I want to get the object (the QWidget Element I clicked on) is there anyway to do that?
I already found some code but I only get the MouseClickEvent from
self.widget_34.mouseReleaseEvent = lambda event: self.myfunction(event)
Although the solution offered by #Cin is interesting, it has a serious problem: it cancels the mousePressEvent of the widget, so the widget loses the behavior it could have when the widget is pressed, for example the button no longer emits the clicked signal, other widget also They will have the same problem.
A less intrusive solution is to use eventFilter:
import sys
import weakref
from PyQt5 import QtCore, QtWidgets
class ClickListener(QtCore.QObject):
clicked = QtCore.pyqtSignal(QtWidgets.QWidget)
def addWidget(self, widget, other_widget=None):
if not hasattr(self, "_widgets"):
self._widgets = {}
widget.installEventFilter(self)
self._widgets[widget] = widget if other_widget is None else other_widget
weakref.ref(widget, self.removeWidget)
def eventFilter(self, obj, event):
if (
obj in self._widgets
and event.type() == QtCore.QEvent.MouseButtonPress
):
self.clicked.emit(self._widgets[obj])
return super(ClickListener, self).eventFilter(obj, event)
def removeWidget(self, widget):
if hasattr(self, "_widgets"):
if widget in self._widgets:
del self._widgets[widget]
class App(QtWidgets.QWidget):
def __init__(self):
super().__init__()
button = QtWidgets.QPushButton("Press Me")
label = QtWidgets.QLabel("Stack Overflow")
spinBox = QtWidgets.QSpinBox()
te = QtWidgets.QTextEdit()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
lay.addWidget(label)
lay.addWidget(spinBox)
lay.addWidget(te)
listener = ClickListener(self)
listener.clicked.connect(self.onClicked)
listener.addWidget(button)
listener.addWidget(label)
listener.addWidget(spinBox.lineEdit(), spinBox)
listener.addWidget(te.viewport(), te)
def onClicked(self, obj):
print("Clicked, from", obj)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
I am not sure this will be a proper solution or not but I think, you can use the partial method of functools module. A collable object can be treated as a function for the purposes of this module. Here you can see my example,
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel
import functools
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(200,200,200,200)
self.button = QPushButton('Button', self)
self.button.move(50,50)
self.label = QLabel(self)
self.label.setText("Label")
self.label.move(100,100)
self.items = [self.button, self.label]
for i in self.items:
i.mousePressEvent = functools.partial(self.getClickedItem, source_object=i)
self.show()
def getClickedItem(self, event, source_object=None):
print("Clicked, from", source_object)
#item text
#print(source_object.text())
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())

Writing a custom QPushButton class in Python

I've recently started learning PyQt on my own and I've come in some trouble trying to write a custom class that inherits from QPushButton so I can adjust its attributes. I'm trying to pass a text as an argument whenever I initialize an object of this class. I am pretty sure there's something wrong with my init but I haven't found it yet.
Here is the code:
import sys
from PySide import QtGui, QtCore
class mainb(QtGui.QPushButton):
def __init__(Text,self, parent = None):
super().__init__(parent)
self.setupbt(Text)
def setupbt(self):
self.setFlat(True)
self.setText(Text)
self.setGeometry(200,100, 60, 35)
self.move(300,300)
print('chegu aqui')
self.setToolTip('Isso é muito maneiro <b>Artur</b>')
self.show()
class mainwindow(QtGui.QWidget):
def __init__(self , parent = None):
super().__init__()
self.setupgui()
def setupgui(self):
self.setToolTip('Oi <i>QWidget</i> widget')
self.resize(800,600)
self.setWindowTitle('Janela do Artur')
af = mainb("Bom dia",self)
self.show()
"""
btn = QtGui.QPushButton('Botão',self)
btn.clicked.connect(QtCore.QCoreApplication.instance().quit)
btn.resize(btn.sizeHint())
btn.move(300, 50)
"""
def main():
app = QtGui.QApplication(sys.argv)
ex = mainwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You are using super in wrong way, super must get a instance and another thing your first arg is Text, that's wrong that should be self. I fixed some more and the below code should work for you
import sys
from PySide import QtGui, QtCore
class mainb(QtGui.QPushButton):
def __init__(self, Text, parent = None):
super(mainb, self).__init__()
self.setupbt(Text)
def setupbt(self, Text):
self.setFlat(True)
self.setText(Text)
self.setGeometry(200,100, 60, 35)
self.move(300,300)
print('chegu aqui')
self.setToolTip('Isso muito maneiro <b>Artur</b>')
self.show()
class mainwindow(QtGui.QWidget):
def __init__(self , parent = None):
super(mainwindow, self).__init__()
self.setupgui()
def setupgui(self):
self.setToolTip('Oi <i>QWidget</i> widget')
self.resize(800,600)
self.setWindowTitle('Janela do Artur')
newLayout = QtGui.QHBoxLayout()
af = mainb("Bom dia",self)
newLayout.addWidget(af)
self.setLayout(newLayout)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = mainwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Your def setupbt(self) does not seem to take the text as argument. Try def setupbt(self, Text): instead.

Python: PyQt QTreeview example - selection

I'm using Python 2.7 and Qt designer and I'm new to MVC:
I have a View completed within Qt to give me a directory tree list, and the controller in place to run things. My question is:
Given a Qtree view, how may I obtain a directory once a dir is selected?
Code snap shot is below, I suspect it's SIGNAL(..) though I'm unsure:
class Main(QtGui.QMainWindow):
plot = pyqtSignal()
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# create model
model = QtGui.QFileSystemModel()
model.setRootPath( QtCore.QDir.currentPath() )
# set the model
self.ui.treeView.setModel(model)
**QtCore.QObject.connect(self.ui.treeView, QtCore.SIGNAL('clicked()'), self.test)**
def test(self):
print "hello!"
The signal you're looking for is selectionChanged emmited by the selectionModel owned by your tree. This signal is emmited with the selected item as first argument and the deselected as second, both are instances of QItemSelection.
So you might want to change the line:
QtCore.QObject.connect(self.ui.treeView, QtCore.SIGNAL('clicked()'), self.test)
to
QtCore.QObject.connect(self.ui.treeView.selectionModel(), QtCore.SIGNAL('selectionChanged()'), self.test)
Also I recommend you to use the new style for signals and slots. Redefine your test function as:
#QtCore.pyqtSlot("QItemSelection, QItemSelection")
def test(self, selected, deselected):
print("hello!")
print(selected)
print(deselected)
Here you have a working example:
from PyQt4 import QtGui
from PyQt4 import QtCore
class Main(QtGui.QTreeView):
def __init__(self):
QtGui.QTreeView.__init__(self)
model = QtGui.QFileSystemModel()
model.setRootPath( QtCore.QDir.currentPath() )
self.setModel(model)
QtCore.QObject.connect(self.selectionModel(), QtCore.SIGNAL('selectionChanged(QItemSelection, QItemSelection)'), self.test)
#QtCore.pyqtSlot("QItemSelection, QItemSelection")
def test(self, selected, deselected):
print("hello!")
print(selected)
print(deselected)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
PyQt5
In PyQt5 is a little bit different (thanks to Carel and saldenisov for comments and aswer.)
... connect moved from being an object method to a method acting upon the attribute when PyQt went from 4 to 5
So instead the known:
QtCore.QObject.connect(self.ui.treeView, QtCore.SIGNAL('clicked()'), self.test)
now you write:
class Main(QTreeView):
def __init__(self):
# ...
self.setModel(model)
self.doubleClicked.connect(self.test) # Note that the the signal is now a attribute of the widget.
Here is a the example (by saldenisov) using PyQt5.
from PyQt5.QtWidgets import QTreeView,QFileSystemModel,QApplication
class Main(QTreeView):
def __init__(self):
QTreeView.__init__(self)
model = QFileSystemModel()
model.setRootPath('C:\\')
self.setModel(model)
self.doubleClicked.connect(self.test)
def test(self, signal):
file_path=self.model().filePath(signal)
print(file_path)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
In PyQt5 it can be done in this way:
from PyQt5.QtWidgets import QTreeView,QFileSystemModel,QApplication
class Main(QTreeView):
def __init__(self):
QTreeView.__init__(self)
model = QFileSystemModel()
model.setRootPath('C:\\')
self.setModel(model)
self.doubleClicked.connect(self.test)
def test(self, signal):
file_path=self.model().filePath(signal)
print(file_path)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
If I understand the question correctly you would like the directory or file name selected.
This is what I do:
from PyQt4 import QtGui
from PyQt4 import QtCore
# ---------------------------------------------------------------------
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(600,400)
self.setWindowTitle("Treeview Example")
self.treeview = QtGui.QTreeView(self)
self.treeview.model = QtGui.QFileSystemModel()
self.treeview.model.setRootPath( QtCore.QDir.currentPath() )
self.treeview.setModel(self.treeview.model)
self.treeview.setColumnWidth(0, 200)
self.setCentralWidget(self.treeview)
self.treeview.clicked.connect(self.on_treeview_clicked)
# ---------------------------------------------------------------------
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_treeview_clicked(self, index):
indexItem = self.treeview.model.index(index.row(), 0, index.parent())
# path or filename selected
fileName = self.treeview.model.fileName(indexItem)
# full path/filename selected
filePath = self.treeview.model.filePath(indexItem)
print(fileName)
print(filePath)
# ---------------------------------------------------------------------
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I tried this alternative to get the file name...
Instead of:
indexItem = self.treeview.model.index(index.row(), 0, index.parent())
# path or filename selected
fileName = self.treeview.model.fileName(indexItem)
I tried:
# path or filename selected
fileName = index.internalPointer().fileName
Which also seems to work...

PyQt technique differences in 2 source code examples

These 2 programs work the same, but there is a small difference in the lines marked with #HERE.
Can someone explain the differences? I do not fully understand what these lines do.
Program 1:
import sys
from PyQt4 import QtGui, QtCore
class myform(QtGui.QDialog):
def __init__(self, parent=None):
super(myform, self).__init__(parent)
form = QtGui.QFormLayout()
form.setHorizontalSpacing(0)
myedit = QtGui.QLineEdit()
form.addWidget(myedit)
self.setLayout(form)
self.setGeometry(300, 300, 400, 0)
self.setWindowTitle('test')
myedit.textChanged.connect(self.editchange) # new style signal slot connections
self.show() # HERE
def editchange(self,data):
print "editchange:", data
if __name__ == "__main__":
app = QtGui.QApplication([])
ex = myform()
#ex.exec_() # HERE
#sys.exit(app.closeAllWindows()) # HERE
sys.exit(app.exec_()) # HERE
Program #2:
import sys
from PyQt4 import QtGui, QtCore
class myform(QtGui.QDialog):
def __init__(self, parent=None):
super(myform, self).__init__(parent)
form = QtGui.QFormLayout()
form.setHorizontalSpacing(0)
myedit = QtGui.QLineEdit()
form.addWidget(myedit)
self.setLayout(form)
self.setGeometry(300, 300, 400, 0)
self.setWindowTitle('test')
myedit.textChanged.connect(self.editchange) # new style signal slot connections
#self.show() # HERE
def editchange(self,data):
print "editchange:", data
if __name__ == "__main__":
app = QtGui.QApplication([])
ex = myform()
ex.exec_() # HERE
sys.exit(app.closeAllWindows()) # HERE
#sys.exit(app.exec_()) # HERE
Program #1 calls exec_ from QApplication (http://pyqt.sourceforge.net/Docs/PyQt4/qapplication.html#exec).
Program #2 calls exec_ from QDialog (http://pyqt.sourceforge.net/Docs/PyQt4/qdialog.html#exec): the resulting dialog is a modal one.
The final behavior is the same because you use a QDialog.
In this case:
app = QtGui.QApplication([])
ex = myform()
ex.show()
app.exec_()
is the same as:
app = QtGui.QApplication([])
ex = myform()
ex.exec_()

Categories

Resources