Center non-editable QComboBox Text with PyQt - python

Is it possible to change the text alignment of a QComboBox that is not not editable? This answer achieves this by making the QComboBox editable
How to center text in QComboBox?
However, I don't want that because with that change, the clickable part of the ComboBox to trigger the list is now only the arrow on the right, whereas before the entire area was clickable and triggers the drop down menu.

The answer is partly already given in the linked question How to center text in QComboBox?
What remains is to make the read-only object clickable. This can be done as suggested here.
...giving you the following code:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QVBoxLayout(self)
self.combo = QtGui.QComboBox()
self.combo.setEditable(True)
self.ledit = self.combo.lineEdit()
self.ledit.setAlignment(QtCore.Qt.AlignCenter)
# as suggested in the comment to
# https://stackoverflow.com/questions/23770287/how-to-center-text-in-qcombobox
self.ledit.setReadOnly(True) #
self.combo.addItems('One Two Three Four Five'.split())
layout.addWidget(self.combo)
self.clickable(self.combo).connect(self.combo.showPopup)
self.clickable(self.ledit).connect(self.combo.showPopup)
def clickable(self,widget):
""" class taken from
https://wiki.python.org/moin/PyQt/Making%20non-clickable%20widgets%20clickable """
class Filter(QtCore.QObject):
clicked = QtCore.pyqtSignal()
def eventFilter(self, obj, event):
if obj == widget:
if event.type() == QtCore.QEvent.MouseButtonRelease:
if obj.rect().contains(event.pos()):
self.clicked.emit()
# The developer can opt for .emit(obj) to get the object within the slot.
return True
return False
filter = Filter(widget)
widget.installEventFilter(filter)
return filter.clicked
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Related

pyqt : Display All values present in QListWidget as selected to user

I have searched a lot but I am unable to find a solution for doing this.
I have a QListWidget which will populate a list of values. I need to implement a "select all" as default behaviour in the list.
I have already used MultiSelection and that works well, but the need is to avoid clicking on each item, in case the user wants to do the update on all items listed.
Can someone help me understand how to do this?
Use QtWidgets.QAbstractItemView.ExtendedSelection
When the user selects an item in the usual way, the selection is cleared and the new item selected. However, if the user presses the Ctrl key when clicking on an item, the clicked item gets toggled and all other items are left untouched. If the user presses the Shift key while clicking on an item, all items between the current item and the clicked item are selected or unselected, depending on the state of the clicked item. Multiple items can be selected by dragging the mouse over them.
import sys
from PyQt5 import QtWidgets
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QListWidget()
for i in range(12):
w.addItem('Item {}'.format(i))
w.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
#w.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
w.show()
sys.exit(app.exec_())
From what I understand you want to implement a function that selects all items, the solution is to iterate using the setSelected() method of QListWidget as shown below:
import sys
from PyQt4 import QtCore, QtGui
class ListWidget(QtGui.QListWidget):
def __init__(self, parent=None):
super(ListWidget, self).__init__(parent)
self.setSelectionMode(QtGui.QListWidget.MultiSelection)
#QtCore.pyqtSlot()
def selectAll(self):
for i in range(self.count()):
it = self.item(i)
if it is not None:
it.setSelected(True)
#QtCore.pyqtSlot()
def clearSelection(self):
for i in range(self.count()):
it = self.item(i)
if it is not None:
it.setSelected(False)
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
lay = QtGui.QVBoxLayout(self)
button_sel = QtGui.QPushButton("Select All")
button_unsel = QtGui.QPushButton("Clear Selection")
self.list_widget = ListWidget()
for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
it = QtGui.QListWidgetItem(letter)
self.list_widget.addItem(it)
button_sel.clicked.connect(self.list_widget.selectAll)
button_unsel.clicked.connect(self.list_widget.clearSelection)
lay.addWidget(button_sel)
lay.addWidget(button_unsel)
lay.addWidget(self.list_widget)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Pyside2 how to get mouse position?

I want to get mouse position in my pyside2 application.(not desktop mouse position that QCursor gives) and I tried two way. Bellow is my code.
import sys
from PySide2 import QtGui, QtWidgets, QtCore
class Palette(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
def mousePressEvent(self, event):
print(event.pos()) # always return (0,0)
print(QtWidgets.QWidget.mapToGlobal(QtCore.QPoint(0, 0))) #makes parameter type error
print(QtWidgets.QWidget.mapToGlobal(QtWidgets.QWidget)) # makes parameter type error
print(QtWidgets.QWidget.mapToGlobal(QtWidgets.QWidget.pos())) # makes parameter type error
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
palette = Palette(self)
view = QtWidgets.QGraphicsView(palette, self)
view.resize(500, 500)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.resize(500, 500)
main_window.show()
app.exec_()
I very wonder how i can get my mouse pos...
From what I understand you want to get the position on the window if you click anywhere in a widget.
To solve the problem, the logic is as follows:
Get the mouse position with respect to the widget
Convert that position to a global position, that is, with respect to the screen.
Convert that global position to a position relative to the window.
For the first step if mousePressEvent() is used, event.pos() returns the position relative to the widget.
For the second step you must convert that position relative to the widget to global with mapToGlobal().
And for the third step mapFromGlobal() of window is used.
def mousePressEvent(self, event):
p = event.pos() # relative to widget
gp = self.mapToGlobal(p) # relative to screen
rw = self.window().mapFromGlobal(gp) # relative to window
print("position relative to window: ", rw)
super(Widget, self).mousePressEvent(event)
Update:
The QGraphicsScene is not a widget, it is not a visual element, although it is part of the representation of a visual element: the QGraphicsView. For you to understand I will explain you with an analogy, let's say that there is a cameraman recording a scene, in that example the QGraphicsScene is the scene and the QGraphicsView is what the camera records, that is, it shows a piece of the QGraphicsScene, so there could be another cameraman recording the scene from another point, so it would show the same scene from another perspective, so the position of the scene depends on the camera, so if your current question would be equivalent to saying which is the position of the point P respect to the camera i-th, and that from the scene is impossible, you should get it from the camera.
So in conclusion you should not use QGraphicsScene but QGraphicsView, the following solutions implement the same logic using 2 different methods:
1. Creating a custom class of QGraphicsView:
import sys
from PySide2 import QtGui, QtWidgets, QtCore
class GraphicsView(QtWidgets.QGraphicsView):
def mousePressEvent(self, event):
p = event.pos() # relative to widget
gp = self.mapToGlobal(p) # relative to screen
rw = self.window().mapFromGlobal(gp) # relative to window
print("position relative to window: ", rw)
super(GraphicsView, self).mousePressEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
scene = QtWidgets.QGraphicsScene(self)
view = GraphicsView(scene, self)
self.setCentralWidget(view)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.resize(500, 500)
main_window.show()
sys.exit(app.exec_())
2. Using eventfilter:
import sys
from PySide2 import QtGui, QtWidgets, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
scene = QtWidgets.QGraphicsScene(self)
self._view = QtWidgets.QGraphicsView(scene, self)
self.setCentralWidget(self._view)
self._view.installEventFilter(self)
def eventFilter(self, obj, event):
if obj is self._view and event.type() == QtCore.QEvent.MouseButtonPress:
p = event.pos() # relative to widget
gp = self.mapToGlobal(p) # relative to screen
rw = self.window().mapFromGlobal(gp) # relative to window
print("position relative to window: ", rw)
return super(MainWindow, self).eventFilter(obj, event)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.resize(500, 500)
main_window.show()
sys.exit(app.exec_())
On the other hand mapToGlobal() is a method that must be called by an instance, when you use QtWidgets.QWidget.mapToGlobal() there is no instance, my question is, what widget do you have the position? the position you have with respect to self, so you must use self.mapToGlobal() , that works for the objects belonging to a class that inherit from QWidget as QGraphicsView, but not in QGraphicsScene since it does not inherit from QWidget, it is not a widget as indicated in the lines above.
I have recently found a more universal way of getting the cursor position, if you don't want to go through sub classing and events.
# get cursor position
cursor_position = QtGui.QCursor.pos()
print cursor_position
### Returns PySide2.QtCore.QPoint(3289, 296)

How to add a second pixmap to an abstract button when pushed in PySide/PyQt

I'm very new to PySide/PyQt environment. I'm trying to make a menu of buttons on top and assign a task to each so that when they are clicked a function draws a painting on the central window. But I also want to make the button change when they are clicked.
I think this might be an straighforward problem to solve if I use QPushButton, but my buttons are images and I'm using the method suggested HERE and use QAbstractButton to create them.
It is mentioned there that
You can add second pixmap and draw it only when the mouse pointer is
hover over button.
And I'm trying to do exactly that. My question is this:
what are possible ways to achieve this? Are the same methods in QPushButtons applicable here? If so, are there any examples of it somewhere?
Here is a snippet of my code:
import sys
from PySide import QtGui, QtCore
BACKGROUND_COLOR = '#808080'
ICON_PATH_ACTIVE = 'icons/activ'
ICON_PATH_PASSIVE = 'icons/pasiv'
class MainWindow(QtGui.QMainWindow):
def __init__(self, app=None):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
dockwidget = QtGui.QWidget()
self.setGeometry(200, 200, 400, 300)
hbox = QtGui.QHBoxLayout()
1_button = PicButton(QtGui.QPixmap("icons/pasiv/1.png"))
2_button = PicButton(QtGui.QPixmap("icons/pasiv/2.png"))
3_button = PicButton(QtGui.QPixmap("icons/pasiv/3.png"))
hbox.addWidget(1_button)
hbox.addWidget(2_button)
hbox.addWidget(3_button)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(hbox)
vbox.setAlignment(hbox, QtCore.Qt.AlignTop)
dockwidget.setLayout(vbox)
self.setCentralWidget(dockwidget)
class PicButton(QtGui.QAbstractButton):
def __init__(self, pixmap, parent=None):
super(PicButton, self).__init__(parent)
self.pixmap = pixmap
self.setFixedSize(100, 100)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(event.rect(), self.pixmap)
def main():
app = QtGui.QApplication(sys.argv)
central = MainWindow()
central.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Thank you.
Use a regular QPushButton with an icon.
iplay = QtGui.QIcon("path/play_icon.png")
ipause = QtGui.QIcon("path/pause_icon.png")
btn = QtGui.QPushButton(ipause, "", None)
def toggle_play():
if btn.icon() == ipause:
btn.setIcon(iplay)
# Do Pause Action
else:
btn.setIcon(ipause)
# Do Play Action
btn.clicked.connect(toggle_play)
btn.show()
If you want hover functionality then you will have to subclass the QPushButton
class MyButton(QtGui.QPushButton):
custom_click_signal = QtCore.Signal()
def enterEvent(self, event):
super().enterEvent(event)
# Change icon hove image here
def leaveEvent(self, event):
super().leaveEvent(event)
# Change icon back to original image here.
def mousePressEvent(self, event):
super().mousePressEvent(event)
self.custom_click_signal.emit()
# connect to signal btn.custom_click_signal.connect(method)
Icons are probably the easiest way instead of manually managing the paint event. There are also mousePressEvent and mouseReleaseEvents if you want the icon to change for someone holding the button down.

Pyside QTreeWidget Double-clicking on multiple items

I've got a qTreeWidget-based interface where I double click on individual items to toggle them on and off. However, I'd like to be able to bulk-toggle them by selecting multiple objects and double clicking them, but when you double click on any item you immediately lose the multi-selection.
Does anyone know a way around this?
Many thanks for your time,
Nick
The first step would be to setup an event that fires whenever an item is double clicked, like so:
treeWidget.itemDoubleClicked.connect(onClickItem)
where onClickItem is:
def onClickItem(item):
print('Text of first column in item is ', item.text(0))
Of course you'll want to do something a bit more fancy inside onClickItem().
The selection/deselection of items is controlled by the mouse-press event, which obviously happens before the double-click is registered. So you need to "eat" the mouse-press at the appropriate moment.
This example allows double-clicking when the meta-key is pressed:
from PySide import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.tree = QtGui.QTreeWidget(self)
for text in 'One Two Three Four'.split():
parent = QtGui.QTreeWidgetItem(self.tree, [text])
for text in 'Red Blue Green'.split():
child = QtGui.QTreeWidgetItem(parent, [text])
parent.setExpanded(True)
self.tree.setSelectionMode(QtGui.QAbstractItemView.MultiSelection)
self.tree.viewport().installEventFilter(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.tree)
def eventFilter(self, source, event):
if (source is self.tree.viewport() and
isinstance(event, QtGui.QMouseEvent) and
event.modifiers() == QtCore.Qt.MetaModifier):
if event.type() == QtCore.QEvent.MouseButtonDblClick:
print('meta-double-click')
return True
if event.type() == QtCore.QEvent.MouseButtonPress:
# kill selection when meta-key is also pressed
return True
return super(Window, self).eventFilter(source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(800, 300, 300, 300)
window.show()
sys.exit(app.exec_())

PyQt: QGraphicsScene events are not propagated down to GraphicItems

I have a GraphicScene inside a QGraphicView populated with many rectangles (items). I would like each rectangle to respond to the mouse click but I'm not able to find the hook to attach the event handler to the correct object and to get the event propagated to it.
I attached an event handler to the scene:
scene.event = myfunction
and it worked (it was firing every event) but I was unable to attach the same function to one of its children. Can you give me an insight on where to search for such an entry point?
So - I'm not really sure what you're doing there, but I can't think of anything in PyQt where you should be mapping a custom function directly to a scene's event method.
Do you have an actual example?
If you're doing:
scene.mousePressEvent = my_mouse_function
Then that is not how you want to do that.
You can look into using an event filters (http://doc.qt.nokia.com/4.7-snapshot/eventsandfilters.html#event-filters).
Best way to get what you want is to subclass the QGraphicsItem (whichever one you are using - QGraphicsRectItem, QGraphicsPathItem, etc.) and overload the mousePressEvent method on it.
http://doc.qt.nokia.com/4.7-snapshot/qgraphicsitem.html#mousePressEvent
For instance:
from PyQt4.QtGui import QGraphicsRectItem
class MyItem(QGraphicsRectItem):
def mousePressEvent(self, event):
super(MyItem, self).mousePressEvent(event)
print 'overloaded'
scene.addItem(MyItem())
Either subclass the view, scene, item etc and reimplement mousePressEvent and/or mouseReleaseEvent; or install an event filter on those items.
For an example that uses an event filter on a scene, see this answer.
Here's a demo which reimplements mouseReleaseEvent on the view:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.view = View(self)
self.label = QtGui.QLabel(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
layout.addWidget(self.label)
class View(QtGui.QGraphicsView):
def __init__(self, parent):
QtGui.QGraphicsView.__init__(self, parent)
self.setScene(QtGui.QGraphicsScene(self))
for index, name in enumerate('One Two Three Four Five'.split()):
item = QtGui.QGraphicsRectItem(
index * 60, index * 60, 50, 50)
item.setData(0, name)
self.scene().addItem(item)
def mouseReleaseEvent(self, event):
pos = event.pos()
item = self.itemAt(pos)
if item is not None:
text = 'Rectangle <b>%s</b>' % item.data(0).toString()
else:
text = 'No Rectangle (%d, %d)' % (pos.x(), pos.y())
self.parent().label.setText(text)
QtGui.QGraphicsView.mouseReleaseEvent(self, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(400, 400)
window.show()
sys.exit(app.exec_())

Categories

Resources