I am trying to simulate a paint brush with Qt buttons instead of pixels. I have overloaded the event filter on each button and can detect eventHover or eventFilter only when the mouse is not pressed down. I want to hold mouse click down and detect if i collide with a button. Any suggestions as to what i should do from here?
def eventFilter(self, a0, a1):
if a1.type() == QtCore.QEvent.Enter:
if(self.mouse.pressed):
ui_logic.square_press(self,"red")
return super().eventFilter(a0, a1)
def square_press(button,color):
style = "QPushButton{background-color:" + color + "}"
button.setStyleSheet(style)
Thank you
What i ended up doing is tracking the mouse and getting the widget at a location.
Example code:
class TrackMouse(QtWidgets.QWidget):
pressed = False
def __init__(self,obj,app):
self.app = app
super(QtWidgets.QWidget, self).__init__()
def eventFilter(self, a0, event):
if event.type() == QtCore.QEvent.MouseMove:
if(event.buttons() == QtCore.Qt.MouseButton.LeftButton):
widget = self.app.widgetAt(event.globalPos())
print(widget.__class__)
if(widget.__class__ is CustomButtom):
ui_logic.square_press(widget,"red")
return super().eventFilter(a0, event)
Related
I used a Python3 script under GTK, where I clicked on a link with the left mouse button to open it in the same window and on a middle mouse button to open it in a separate window.
I am now in the process of migrating this script from GTK to Qt6.
The problem is that I cannot intercept the middle mouse button in conjunction with a clicked link:
My browser window is based on QWebEngineView, I can intercept mouse button clicks with an event filter (eventFilter()), and I can intercept clicked links within the acceptNavigationRequest() function of a QWebEnginePage.
The URL of the clicked link is only available in acceptNavigationRequest(), but this function is only called by Qt6 when I use the left mouse button, not, as desired, the middle mouse button (neither does control + left mouse button work).
How can I let a middle mouse button click be recognized in a navigation request?
Debian 11.6 Bullseye, Python 3.9.2, PyQt6-Qt6 6.4.1
With the help of #musicamante (note the comments in the original post), I solved my problem this way:
g_mouse_button = '--'
class MyWebEngineView(QWebEngineView):
def __init__(self):
...
class MyEventFilter(QObject):
def eventFilter(self, obj, event):
global g_mouse_button
...
if event.button() == Qt.MouseButton.MiddleButton:
g_mouse_button = 'middle'
...
...
class MyWebEnginePage(QWebEnginePage):
def acceptNavigationRequest(self, url, ttype, isMainFrame):
global g_mouse_button
if ttype == QWebEnginePage.NavigationType.NavigationTypeLinkClicked and \
g_mouse_button == 'middle':
<open link in new window>
g_mouse_button = '--'
return False # ignore
return True # process
def createWindow(self, window_type):
return self
if __name__ == "__main__":
...
event_filter = MyEventFilter()
g_app.installEventFilter(event_filter)
...
I would like "Button" object to disappear when "Target" object is not in focus (for example, when object "Secondary" is focused) and to re-appear when "Target" is in focus again. So, "Target" focused = "Button" visible. In other words, in the code below there are two lines, "Line A" and "Line B", that I would like to implement in the code.
`
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit
class Wn(QWidget):
def __init__(self):
super().__init__()
self.target = Target("Target", self)
self.target.setFixedSize(400, 60)
self.target.move(50, 50)
self.secondary = QLineEdit("Secondary", self)
self.secondary.setFixedSize(400, 60)
self.secondary.move(50, 150)
self.button = QPushButton("Appears # Target focused. Disappears # target not focused", self)
self.button.setFixedSize(400, 60)
self.button.move(50, 250)
class Target(QLineEdit):
def focusInEvent(self, k):
print("The target is in focus: Button should be shown")
self.setStyleSheet("background-color: red;")
# Wn.button.setHidden(False) # Line A
def focusOutEvent(self, p):
print("The target is out of focus: Button should be hidden")
self.setStyleSheet("background-color: white;")
# Wn.button.setHidden(True) # Line B
app = QApplication(sys.argv)
wn = Wn()
wn.show()
sys.exit(app.exec())
`
You can create a signal and emit it whenever the focus changes, then connect it with the button's setVisible().
class Wn(QWidget):
def __init__(self):
# ...
self.target.focusChanged.connect(self.button.setVisible)
class Target(QLineEdit):
focusChanged = pyqtSignal(bool)
def focusInEvent(self, k):
super().focusInEvent(k)
print("The target is in focus: Button should be shown")
self.setStyleSheet("background-color: red;")
self.focusChanged.emit(True)
def focusOutEvent(self, p):
super().focusOutEvent(p)
print("The target is out of focus: Button should be hidden")
self.setStyleSheet("background-color: white;")
self.focusChanged.emit(False)
Alternatively, you can just install an event filter on the line edit and look for FocusIn and FocusOut events.
Note that you should always call the base implementation of event handler overrides, unless you really know what you're doing, otherwise you might prevent proper default behavior of the object.
Also, layout managers should always be used instead of fixed geometries. Since the visibility of a widget also nullifies its size in the layout and adapts the other widgets managed by it (similarly to display: none in CSS), you should probably consider using setRetainSizeWhenHidden() for the widget's size policy:
class Wn(QWidget):
def __init__(self):
# ...
# create a proper layout and add widgets
# ...
policy = self.button.sizePolicy()
policy.setRetainSizeWhenHidden(True)
self.button.setSizePolicy(policy)
I tried to use setToolTip of QGroupBox, but the tooltip shows in all places in the group box. What I want is to only show the tooltip in the title label.
Is is even possible? If not, why the QGroupBox is designed that way?
It is possible to control the tool-tip behaviour, but there's no built-in method to do that, so you just need to add a little custom event-handling yourself. Here's a basic demo that implements that using an event-filter:
from PyQt5 import QtCore, QtGui, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton('Test')
self.button.setToolTip('Button ToolTip')
self.group = QtWidgets.QGroupBox('Title')
self.group.installEventFilter(self)
self.group.setToolTip('Groupbox ToolTip')
self.group.setCheckable(True)
hbox = QtWidgets.QHBoxLayout(self.group)
hbox.addWidget(self.button)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.group)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.ToolTip and
isinstance(source, QtWidgets.QGroupBox)):
options = QtWidgets.QStyleOptionGroupBox()
source.initStyleOption(options)
control = source.style().hitTestComplexControl(
QtWidgets.QStyle.CC_GroupBox, options, event.pos())
if (control != QtWidgets.QStyle.SC_GroupBoxLabel and
control != QtWidgets.QStyle.SC_GroupBoxCheckBox):
QtWidgets.QToolTip.hideText()
return True
return super().eventFilter(source, event)
if __name__ == '__main__':
app = QtWidgets.QApplication(['Test'])
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
app.exec_()
An alternative solution would be to create a subclass and override the event method directly:
class GroupBox(QtWidgets.QGroupBox):
def event(self, event):
if event.type() == QtCore.QEvent.ToolTip:
options = QtWidgets.QStyleOptionGroupBox()
self.initStyleOption(options)
control = self.style().hitTestComplexControl(
QtWidgets.QStyle.CC_GroupBox, options, event.pos())
if (control != QtWidgets.QStyle.SC_GroupBoxLabel and
control != QtWidgets.QStyle.SC_GroupBoxCheckBox):
QtWidgets.QToolTip.hideText()
return True
return super().event(event)
QWidget::setToolTip, by defaults, set the tooltip for the whole widget. QGroupBox does not provide a special tooltip behavior, so the tooltip is visible for the whole group box, including its children (except if those provide their own tooltips). However, you are ably to customise this behaviour yourself, by reimplementing QWidget::event:
If you want to control a tooltip's behavior, you can intercept the
event() function and catch the QEvent::ToolTip event (e.g., if you
want to customize the area for which the tooltip should be shown).
Two approaches are possible:
Capture the QEvent::ToolTip event on the QGroupBox itself. See the comment of musicamante on how to determine the title region.
Capture the QEvent::ToolTip on the children of the QGroupBox and suppress the the event.
A full blown custom tooltip example is given in the Qt documentation itself.
I was working on a PySide interface for Maya and i was wondering if its possible to define a NON RECTANGULAR clickeable area for a button.
I tried using QPushButton and also extending a QLabel object to get button behavior but do you know if its possible to get a button containing a picture with alpha channel and use that alpha to define the click area for a button?
I'd appreciate a lot if you can guide me through how to solve this problem.
Thanks in advance.
I've tried this...
from PySide import QtCore
from PySide import QtGui
class QLabelButton(QtGui.QLabel):
def __init(self, parent):
QtGui.QLabel.__init__(self, parent)
def mousePressEvent(self, ev):
self.emit(QtCore.SIGNAL('clicked()'))
class CustomButton(QtGui.QWidget):
def __init__(self, parent=None, *args):
super(CustomButton, self).__init__(parent)
self.setMinimumSize(300, 350)
self.setMaximumSize(300, 350)
picture = __file__.replace('qbtn.py', '') + 'mario.png'
self.button = QLabelButton(self)
self.button.setPixmap(QtGui.QPixmap(picture))
self.button.setScaledContents(True)
self.connect(self.button, QtCore.SIGNAL('clicked()'), self.onClick)
def onClick(self):
print('Button was clicked')
if __name__ == '__main__':
app = QApplication(sys.argv)
win = CustomButton()
win.show()
app.exec_()
sys.exit()
mario.png
This is the final code i get to solve my above question...
from PySide import QtCore
from PySide import QtGui
class QLabelButton(QtGui.QLabel):
def __init(self, parent):
QtGui.QLabel.__init__(self, parent)
def mousePressEvent(self, ev):
self.emit(QtCore.SIGNAL('clicked()'))
class CustomButton(QtGui.QWidget):
def __init__(self, parent=None, *args):
super(CustomButton, self).__init__(parent)
self.setMinimumSize(300, 350)
self.setMaximumSize(300, 350)
pixmap = QtGui.QPixmap('D:\mario.png')
self.button = QLabelButton(self)
self.button.setPixmap(pixmap)
self.button.setScaledContents(True)
self.button.setMask(pixmap.mask()) # THIS DOES THE MAGIC
self.connect(self.button, QtCore.SIGNAL('clicked()'), self.onClick)
def onClick(self):
print('Button was clicked')
You can do this by catching the press/release events and checking the position of the click with the value of the pixel in the image to decide if the widget should emit a click or not.
class CustomButton(QWidget):
def __init__(self, parent, image):
super(CustomButton, self).__init__(parent)
self.image = image
def sizeHint(self):
return self.image.size()
def mouseReleaseEvent(self, event):
# Position of click within the button
pos = event.pos()
# Assuming button is the same exact size as image
# get the pixel value of the click point.
pixel = self.image.alphaChannel().pixel(pos)
if pixel:
# Good click, pass the event along, will trigger a clicked signal
super(CustomButton, self).mouseReleaseEvent(event)
else:
# Bad click, ignore the event, no click signal
event.ignore()
I have to create one screen in Qt in which I have to show a remote having lots of buttons in it and when user clicks some button on actual remote, corresponding button in the image get highlighted. So what I have done is, I have used QLabel and set the remote image as background image and then I have put small rectangular label for each button and filled them with semi transparent color and when user click button in actual remote label color changes, but by using this method lot of labels are getting used making code looking inefficient, so I was thinking of drawing on QLabel (which has a remote as background image) over buttons.
Can anybody suggest me, which API of Qt should I use, and how to follow up on this?
I believe QGraphics is the correct route for a completely custom graphical interface, but if you want to try something that doesn't require you to change too much of your existing approach, you can do a widget with a custom paint event:
This is written in PyQt but you can easily translate to Qt
from PyQt4 import QtCore, QtGui
class LabelButton(QtGui.QWidget):
clicked = QtCore.pyqtSignal()
def __init__(self, labelStr, pixStr, parent=None):
super(LabelButton, self).__init__(parent)
self.label = labelStr
self.pix = QtGui.QPixmap(pixStr)
def paintEvent(self, event):
super(LabelButton, self).paintEvent(event)
rect = event.rect()
painter = QtGui.QPainter(self)
painter.drawPixmap(rect, self.pix)
pos = (rect.bottomLeft()+rect.bottomRight()) / 2
pos.setY(pos.y()-10)
painter.drawText(pos, self.label)
painter.end()
def mousePressEvent(self, event):
event.accept()
self.clicked.emit()
def handleClick():
print "CLICK"
if __name__ == "__main__":
app = QtGui.QApplication([])
widget = LabelButton("A Text Label", "myImage.png")
widget.resize(600,400)
widget.show()
widget.raise_()
widget.clicked.connect(handleClick)
app.exec_()
This is a rough example. You can get more fine tuned with the drawing of the text. This widget takes a label string, and a picture path, and will paint the picture as the background, and the text as a label. You can do any number of things with this custom widget in both the paint event, and with custom signals and events.
I have used this code to Draw over Image in Label:
Image is loaded in Ui and the Code is as follows In paintevent
void ColorTab::paintEvent(QPaintEvent *e){
ui->lbl_capture_img->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
ui->Lbl_color_tab_WG->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
Cap_Image = QImage(ui->lbl_capture_img->pixmap()->toImage());
Lbl_Image = QImage(ui->Lbl_color_tab_WG->pixmap()->toImage());
QPainter painter_Lbl(&Lbl_Image);
QPainter painter_Cap(&Cap_Image);
QPen pen(Qt::white, 5, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin);
painter_Lbl.setPen(pen);
painter_Cap.setPen(pen);
painter_Lbl.drawPolygon(blinkRect_Lbl);
painter_Cap.drawPolygon(blinkRect_Cap);
ui->lbl_capture_img->setPixmap(QPixmap::fromImage(Cap_Image));
ui->Lbl_color_tab_WG->setPixmap(QPixmap::fromImage(Lbl_Image));
painter_Cap.end();
painter_Lbl.end();
}