PySide get width of arrow button in QSpinBox - python

Is there a way to determine the width of the arrow buttons in the qspinbox?
I'm trying to overwrite the context menu event, and i only want my custom event to take place if the user right-clicks over the arrow button, otherwise i want the normal context menu to appear.
Right now I'm just hardcoding a value of 20 which is not ideal.
import sys
import os
from PySide import QtGui, QtCore
class MySpinner(QtGui.QSpinBox):
def __init__(self, parent=None):
super(MySpinner, self).__init__(parent)
self.setAccelerated(False)
self.setRange(-1000,1000)
self.setSingleStep(1)
self.setValue(300)
def contextMenuEvent(self, event):
if event.pos().x() > self.rect().right()-20:
self.setValue(50)
self.selectAll()
else:
super(self.__class__, self).contextMenuEvent(event)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(300, 200)
grid = QtGui.QVBoxLayout()
grid.addWidget(MySpinner())
content = QtGui.QWidget()
content.setLayout(grid)
self.setCentralWidget(content)
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Instead of obtaining the width it is only necessary to obtain the SubControl to know if it was pressed in one of the arrows buttons:
def contextMenuEvent(self, event):
opt = QtGui.QStyleOptionSpinBox()
self.initStyleOption(opt)
opt.subControls = QtGui.QStyle.SC_All
hoverControl = self.style().hitTestComplexControl(QtGui.QStyle.CC_SpinBox, opt, event.pos(), self)
if hoverControl == QtGui.QStyle.SC_SpinBoxUp:
print("up")
elif hoverControl == QtGui.QStyle.SC_SpinBoxDown:
print("down")
else:
super(self.__class__, self).contextMenuEvent(event)
If you want to get the QRect of each subcontrol you should use
# up
rect_up = self.style().subControlRect(QtGui.QStyle.CC_SpinBox, opt, QtGui.QStyle.SC_SpinBoxUp, self)
# down
rect_down = self.style().subControlRect(QtGui.QStyle.CC_SpinBox, opt, QtGui.QStyle.SC_SpinBoxDown, self)
Another option:
def contextMenuEvent(self, event):
opt = QtGui.QStyleOptionSpinBox()
self.initStyleOption(opt)
r = QtCore.QRect()
for sc in (QtGui.QStyle.SC_SpinBoxUp, QtGui.QStyle.SC_SpinBoxDown):
r= r.united(self.style().subControlRect(QtGui.QStyle.CC_SpinBox, opt, sc, self))
if r.contains(event.pos()):
print("arrow")
else:
super(self.__class__, self).contextMenuEvent(event)

Related

Move focus to another widget when the return or right arrow key is pressed?

I have a Qt widget that looks like this:
class launchiiwidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.textbox = QtWidgets.QTextEdit(self)
self.textbox.setLineWrapMode(QtWidgets.QTextEdit.NoWrap)
self.textbox.setAlignment(QtCore.Qt.AlignCenter)
self.textbox.setFixedSize(QtCore.QSize(600, 100))
self.textbox.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.textbox.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
layout.addWidget(self.textbox)
font = self.textbox.font()
font.setPointSize(80)
self.textbox.setFont(font)
self.listwidget = QtWidgets.QListWidget(self)
self.listwidget.addItem("Red")
self.listwidget.addItem("Blue")
layout.addWidget(self.listwidget)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = launchiiwidget()
widget.setWindowFlags(QtCore.Qt.FramelessWindowHint)
widget.resize(600, 200)
widget.show()
sys.exit(app.exec())
How can I make it so when the "return" or "right arrow key" is pressed, focus moves from wherever it is currently to the first item in listwidget? This should also work while being focused inside of textbox, without triggering a newline.
Note: items get dynamically added to listwidget.
A possible solution could be to use QShorcut but because the OP requires "without triggering a newline". So in this case the solution is to implement an eventfilter to the QWindow:
import sys
from PyQt6 import QtCore, QtGui, QtWidgets
class KeyHelper(QtCore.QObject):
pressed = QtCore.pyqtSignal()
def __init__(self, window):
super().__init__(window)
self._window = window
self.window.installEventFilter(self)
#property
def window(self):
return self._window
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.Type.KeyPress:
if event.key() in (
QtCore.Qt.Key.Key_Return,
QtCore.Qt.Key.Key_Enter,
QtCore.Qt.Key.Key_Right,
):
self.pressed.emit()
return True
return super().eventFilter(obj, event)
class Launchiiwidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.textbox = QtWidgets.QTextEdit()
self.textbox.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
self.textbox.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.textbox.setFixedSize(QtCore.QSize(600, 100))
self.textbox.setVerticalScrollBarPolicy(
QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff
)
self.textbox.setHorizontalScrollBarPolicy(
QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff
)
font = self.textbox.font()
font.setPointSize(80)
self.textbox.setFont(font)
self.listwidget = QtWidgets.QListWidget()
self.listwidget.addItem("Red")
self.listwidget.addItem("Blue")
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.textbox)
layout.addWidget(self.listwidget)
def update_focus(self):
self.listwidget.setFocus()
index = self.listwidget.model().index(0, 0)
if index.isValid():
self.listwidget.setCurrentIndex(index)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = Launchiiwidget()
widget.setWindowFlags(QtCore.Qt.WindowType.FramelessWindowHint)
widget.resize(600, 200)
widget.show()
key_helper = KeyHelper(widget.windowHandle())
key_helper.pressed.connect(widget.update_focus)
sys.exit(app.exec())

PyQt5 context menu for QTableWidget column head

is there a way to get a context menu on a tables column head.
Find nothing about that in PyQt5's tuts.
the table's context menu is simple but the column heads don't affect.
# dlg is a QDialog object
self.tbl = QtWidgets.QTableWidget(dlg)
self.tbl.setContextMenuPolicy( Qt.CustomContextMenu )
You have to use the QHeaderView of the QTableWidget:
from PyQt5 import QtCore, QtWidgets
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.tbl = QtWidgets.QTableWidget(10, 10, self)
for w in (self.tbl.horizontalHeader(), self.tbl.verticalHeader(), self.tbl):
w.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
w.customContextMenuRequested.connect(self.on_customContextMenuRequested)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.tbl)
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested(self, pos):
widget = self.sender()
if isinstance(widget, QtWidgets.QAbstractItemView):
widget = widget.viewport()
menu = QtWidgets.QMenu()
menu.addAction("Foo Action")
menu.exec_(widget.mapToGlobal(pos))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Dialog()
w.show()
sys.exit(app.exec_())
Update:
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.tbl = QtWidgets.QTableWidget(10, 10, self)
self.tbl.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.customContextMenuRequested.connect(self.on_customContextMenuRequested_tw)
self.tbl.verticalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.verticalHeader().customContextMenuRequested.connect(self.on_customContextMenuRequested_vh)
self.tbl.horizontalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.horizontalHeader().customContextMenuRequested.connect(self.on_customContextMenuRequested_hh)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.tbl)
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_tw(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action TW")
menu.exec_(self.tbl.viewport().mapToGlobal(pos))
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_vh(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action VH")
menu.exec_(self.tbl.verticalHeader().mapToGlobal(pos))
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_hh(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action HH")
menu.exec_(self.tbl.horizontalHeader().mapToGlobal(pos))
You need to set the context menu policy on the header itself (if I've understood correctly), so...
self.tbl = QtWidgets.QTableWidget(dlg)
self.tbl.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu)
and connect to the `QHeaderView::customContextMenuRequested signal...
self.tbl.horizontalHeader().customContextMenuRequested.connect(self.handle_context_menu_request)

PyQt5 Automatic drawing from input value

I have recently created a widget with Qpaint, which I want to pass value to it, at the same time force the Qpaint Widget to draw from input values. The idea is to define a data value from a Qdialog and pass it to main widget, and pass the value to Qpaint Widget class. I would like to have, when user clicks on the button 'Getting values' a dialog widget would appear and insert some int values, then pass it to main Widget. from there pass value to correct class Paint. Which would draw and display the result. I tried with Qlabel, to assign value first to Qlabel or QlineEdit,
class Button(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
---------
self.value = QtWidgets.QLabel()
--------
Then inside the paint class call the value or text of those. then assign it to Qpaint event. But seems does not work.'
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.button = Button()
self.Value = self.button.value
---------
painter.drawRect(100,100,250,250) <----- instead of value 250 having self.Value
The code Main.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from datainput import *
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 800, 800))
self.button = Button()
self.paint = Paint()
self.lay = QtWidgets.QVBoxLayout()
self.lay.addWidget(self.paint)
self.lay.addWidget(self.button)
self.setLayout(self.lay)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.button = Button()
self.Value = self.button.value
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
def paintEvent(self, event):
self.pen = QtGui.QPen()
self.brush = QtGui.QBrush( QtCore.Qt.gray, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(self.pen)
painter.setBrush(self.brush)
painter.drawRect(100,100,250,250)
painter.setBrush(QtGui.QBrush())
class Button(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
getbutton = QtWidgets.QPushButton('Getting values')
Alay = QtWidgets.QVBoxLayout(self)
Alay.addWidget(getbutton)
self.value = QtWidgets.QLabel()
getbutton.clicked.connect(self.getbuttonfunc)
def getbuttonfunc(self):
subwindow=Dinput()
subwindow.setWindowModality(QtCore.Qt.ApplicationModal)
if subwindow.exec_() == QtWidgets.QDialog.Accepted:
self._output = subwindow.valueEdit.text()
return self.value.setText(self._output)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())
Input Qdialog code, datainput.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Dinput(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dinput, self).__init__(parent)
valuelabel = QtWidgets.QLabel('Input: ')
self.valueEdit = QtWidgets.QLineEdit()
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.close)
self.Alay = QtWidgets.QHBoxLayout()
self.Alay.addWidget(valuelabel)
self.Alay.addWidget(self.valueEdit)
self.Blay = QtWidgets.QVBoxLayout()
self.Blay.addLayout(self.Alay)
self.Blay.addWidget(buttonBox)
self.setLayout(self.Blay)
def closeEvent(self, event):
super(Dinput, self).closeEvent(event)
def accept(self):
super(Dinput, self).accept()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Dinput()
w.show()
sys.exit(app.exec_())
Visualization
I appreciate any help. Thankssss
datainput is irrelevant, your task is only to obtain a number so for space question I will not use it and instead I will use QInputDialog::getInt(). Going to the problem, the strategy in these cases where the value can be obtained at any time is to notify the change to the other view through a signal, in the slot that receives the value is to update a variable that stores the value and call update so that it calls when necessary to paintEvent, and in the paintEvent use the variable that stores the value.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 800, 800))
self.button = Button()
self.paint = Paint()
self.button.valueChanged.connect(self.paint.set_size_square)
self.lay = QtWidgets.QVBoxLayout(self)
self.lay.addWidget(self.paint)
self.lay.addWidget(self.button)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
self._size_square = 250
#QtCore.pyqtSlot(int)
def set_size_square(self, v):
self._size_square = v
self.update()
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush( QtCore.Qt.gray, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(pen)
painter.setBrush(brush)
r = QtCore.QRect(QtCore.QPoint(100, 100), self._size_square*QtCore.QSize(1, 1))
painter.drawRect(r)
class Button(QtWidgets.QWidget):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(Button, self).__init__(parent)
getbutton = QtWidgets.QPushButton('Getting values')
Alay = QtWidgets.QVBoxLayout(self)
Alay.addWidget(getbutton)
self.value = QtWidgets.QLabel()
getbutton.clicked.connect(self.getbuttonfunc)
#QtCore.pyqtSlot()
def getbuttonfunc(self):
number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
self.tr("Input:"), 1, 1)
if ok:
self.valueChanged.emit(number)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())

PyQt Right click Menu for QComboBox

I'm trying to extend the QComboClass with a right click menu, and offer it an option to set the current index to -1 (clearing the selection). I'm having trouble invoking the context menu or even the right click event.
class ComboBox(QComboBox):
def __init__(self, *args, **kwargs):
super(ComboBox, self).__init__()
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.showMenu)
def showMenu(self, pos):
menu = QMenu()
clear_action = menu.addAction("Clear Selection", self.clearSelection)
action = menu.exec_(self.mapToGlobal(pos))
def clearSelection(self):
self.setCurrentIndex(-1)
Could someone tell me what I'm doing wrong?
can you try this,
def showMenu(self,event):
menu = QMenu()
clear_action = menu.addAction("Clear Selection", self)
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == clear_action:
self.clearSelection()
You can try this
import sys
from PyQt4 import QtGui
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QMenu
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.lbl = QtGui.QLabel("Ubuntu", self)
self.combo = QtGui.QComboBox(self)
self.combo.setContextMenuPolicy(Qt.CustomContextMenu)
self.combo.customContextMenuRequested.connect(self.showMenu)
self.combo.addItem("Ubuntu")
self.combo.addItem("Mandriva")
self.combo.addItem("Fedora")
self.combo.addItem("Red Hat")
self.combo.addItem("Gentoo")
self.combo.move(50, 50)
self.lbl.move(50, 150)
self.combo.activated[str].connect(self.onActivated)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('QtGui.QComboBox')
self.show()
def showMenu(self,pos):
menu = QMenu()
clear_action = menu.addAction("Clear Selection")
action = menu.exec_(self.mapToGlobal(pos))
if action == clear_action:
self.combo.setCurrentIndex(0)
def onActivated(self, text):
self.lbl.setText(text)
self.lbl.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Why is my QGraphicsItem not selectable?

I copied some code snippets and made my own version of it. The initial snippet (which I don't have anymore) allowed to move and also select a QgraphicsItem. My modified version allows movement, but not selecting. What am I doing wrong?
#!d:/python27/python -u
import sys
from PyQt4 import QtGui, QtCore
class GraphicsItem(QtGui.QGraphicsItem):
#
# QtGui.QGraphicsItem always needs to override its two public abstract methods
# paint, boundingRect
#
def __init__(self, rect=None, parent=None):
super(GraphicsItem, self).__init__(parent)
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
self.pen = QtGui.QPen(QtCore.Qt.SolidLine)
self.pen.setColor(QtCore.Qt.blue)
self.pen.setWidth(8)
self.brush = QtGui.QBrush(QtCore.Qt.red)
self.rect = QtCore.QRectF(rect[0], rect[1], rect[2], rect[3])
def mouseMoveEvent(self, event):
# move object
QtGui.QGraphicsItem.mouseMoveEvent(self, event)
def boundingRect(self):
return self.rect
def paint(self, painter, option, widget):
painter.setBrush(self.brush)
painter.setPen(self.pen)
painter.drawEllipse(self.rect)
class MyMainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
width = 1000
height = 800
scene = QtGui.QGraphicsScene(-width/2, -height/2, width, height)
graphicsItem = GraphicsItem((-100, -100, 200, 200))
scene.addItem(graphicsItem)
view = QtGui.QGraphicsView()
# set QGraphicsView attributes
view.setRenderHints(QtGui.QPainter.Antialiasing |
QtGui.QPainter.HighQualityAntialiasing)
view.setViewportUpdateMode(QtGui.QGraphicsView.FullViewportUpdate)
view.setScene(scene)
self.setCentralWidget(view)
def keyPressEvent(self, event):
key = event.key()
if key == QtCore.Qt.Key_Escape:
sys.exit(QtGui.qApp.quit())
else:
super(GraphicsView, self).keyPressEvent(event)
def main():
app = QtGui.QApplication(sys.argv)
form = MyMainWindow()
form.setGeometry(700, 100, 1050, 850)
form.show()
app.exec_()
if __name__ == '__main__':
main()
You miss this method in class GraphicsItem:
def mousePressEvent(self, event):
# select object
QtGui.QGraphicsItem.mousePressEvent(self, event)
print (self) # show the selected item

Categories