Is there a simple way to check if the up or down button of a QT spinbox is pressed? I've seen this in a forum on QT but don't know how to deconstruct it or is far complex for me to understand and I'm coding in PyQT5.
void MySpinBox::mousePressEvent(QMouseEvent* event)
QSpinBox::mousePressEvent(event);
QStyleOptionSpinBox opt;
this->initStyleOption(&opt);
if( this->style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxUp).contains(event->pos()) )
// UP BUTTON PRESSED
else if( this->style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxDown).contains(event->pos()) )
//DOWN BUTTON PRESSED
Also I have this 2 variables that contain the spinbox value
version_spinValue = self.ui.version_sbox.value()
work_spinValue = self.ui.work_sbox.value()
All I want to do is when the version value spinbox up button is pressed, the work spinbox value resets to 1, and when it goes down, it just do nothing (or just print a simple text that the down button is pressed).
You have to use the hitTestComplexControl() method to know which element was clicked:
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QApplication, QSpinBox, QStyle, QStyleOptionSpinBox
class SpinBox(QSpinBox):
upClicked = pyqtSignal()
downClicked = pyqtSignal()
def mousePressEvent(self, event):
super().mousePressEvent(event)
opt = QStyleOptionSpinBox()
self.initStyleOption(opt)
control = self.style().hitTestComplexControl(
QStyle.CC_SpinBox, opt, event.pos(), self
)
if control == QStyle.SC_SpinBoxUp:
self.upClicked.emit()
elif control == QStyle.SC_SpinBoxDown:
self.downClicked.emit()
def main():
import sys
app = QApplication(sys.argv)
w = SpinBox()
w.upClicked.connect(lambda: print("up"))
w.downClicked.connect(lambda: print("down"))
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Related
I want to make a simple browser GUI for learning purposes with PyQt5. A function that I want is to have in the status bar a text "Online". If the user clicks somewhere else from the browser and the application loses focus, a message will appear in the status bar indicating that, and after a few seconds the browser will change the url to google.
If I run the following code everything works fine as expected, when app loses focus it navigates to google. However, the message "Application lost focus ..." doesn't appear in the statusbar. It simply skips that line. If I remove the seturl and time.sleep line, the script will change the text as expected.
Why is it skipping that line? (Line 55)
import sys
import time
from PyQt5.QtGui import QIcon, QFont
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox, QFrame ,QMainWindow, QLabel
from PyQt5.QtWebEngineWidgets import *
class VLine(QFrame):
# a simple VLine, like the one you get from designer
def __init__(self):
super(VLine, self).__init__()
self.setFrameShape(self.VLine|self.Sunken)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setFocus()
app.focusChanged.connect(self.on_focusChanged)
self.browser = QWebEngineView()
self.browser.setContextMenuPolicy(Qt.PreventContextMenu)
self.browser.setUrl(QUrl('http://stackoverflow.com'))
self.setCentralWidget(self.browser)
self.showMaximized()
self.date = QDate.currentDate()
font = QFont('Arial', 16, QFont.Bold)
self.statusBar().setFont(font)
timer = QTimer(self)
timer.timeout.connect(self.showTime)
timer.start(1000)
self.lbl1 = QLabel(self)
self.lbl1.setStyleSheet('border: 0; color: red;')
self.lbl1.setFont(font)
self.statusBar().reformat()
self.statusBar().setStyleSheet('border: 0; background-color: #FFF8DC;')
self.statusBar().setStyleSheet("QStatusBar::item {border: none;}")
self.statusBar().addPermanentWidget(VLine()) # <---
self.statusBar().addPermanentWidget(self.lbl1)
self.statusBar().addPermanentWidget(VLine())
def showTime(self):
current_time = QTime.currentTime()
label_time = current_time.toString('hh:mm:ss')
self.statusBar().showMessage('Time: ' + label_time + ' || Date: ' + self.date.toString('dd.MM.yyyy'))
def on_focusChanged(self):
if self.isActiveWindow() == False:
print(f"\nwindow is the active window: {self.isActiveWindow()}")
self.lbl1.setText('Application lost focus. Returning to Google in 5 seconds')
time.sleep(5)
self.browser.setUrl(QUrl('http://google.com'))
self.lbl1.setText('Online')
else:
print(f"window is the active window: {self.isActiveWindow()}")
self.lbl1.setText('Online')
app = QApplication(sys.argv)
QApplication.setApplicationName('Browser')
window = MainWindow()
app.exec_()
You are confusing how the focusChanged signal works: it emits a signal regarding the focus changes within the program.
What you need is to override the changeEvent of the window and intercept an ActivationChange event type.
class MainWindow(QMainWindow):
# ...
def changeEvent(self, event):
if event.type() == event.ActivationChange:
# ...
That said, NEVER put a blocking function within the main thread.
Remove the time.sleep and never think about using it again for this kind of things.
Add a function for that redirect, and create a QTimer that you can start when losing focus (and stop if regaining it again before the timeout).
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.focusLostTimer = QTimer(
interval=5000, singleShot=True, timeout=self.focusRedirect)
def focusRedirect(self):
self.browser.setUrl(QUrl('http://google.com'))
def changeEvent(self, event):
if event.type() == event.ActivationChange:
if not self.isActiveWindow():
self.focusLostTimer.start()
else:
self.focusLostTimer.stop()
I have a QComboBox and I need to set up a list of names when the drop-down arrow of that QComboBox is clicked. So is there any function of PySide2 to find out whether the user has clicked that drop-down arrow, after this I would like to get the index of the user selection. If anyone has any idea about doing this either in PySide2.
You have to detect the position of the mouse click and verify that the complexcontrol is QStyle::SC_ComboBoxArrow:
import sys
from PySide2 import QtCore, QtGui, QtWidgets
class ComboBox(QtWidgets.QComboBox):
arrowClicked = QtCore.Signal()
def mousePressEvent(self, event):
super().mousePressEvent(event)
opt = QtWidgets.QStyleOptionComboBox()
opt.initFrom(self)
opt.subControls = QtWidgets.QStyle.SC_All
opt.activeSubControls = QtWidgets.QStyle.SC_None
opt.editable = self.isEditable()
cc = self.style().hitTestComplexControl(
QtWidgets.QStyle.CC_ComboBox, opt, event.pos(), self
)
if cc == QtWidgets.QStyle.SC_ComboBoxArrow:
self.arrowClicked.emit()
def main():
app = QtWidgets.QApplication(sys.argv)
w = ComboBox()
w.addItems(["option1", "option2", "option3"])
w.show()
w.arrowClicked.connect(
lambda: print("index: {}, value: {}".format(w.currentIndex(), w.currentText()))
)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I am catching key press events by overriding the KeyPressEvent method of my class, inheriting from QWidget, but I cannot see key press events for the ESCAPE and BACKSPACE keys, as well as C-x or again C-c. I can see key release events though. Who catches them ?
class KeyCaptureWidget(QWidget):
# init…
def keyPressEvent(self, event):
key = event.key()
logging.info("key press: {}".format(key))
def keyReleaseEvent(self, event):
key_code = event.key()
logging.info("key release: {}".format(key_code))
The difference between C-x and say C-b is the following:
pressing C-b prints: control is pressed, b is pressed, b is released, control is released.
pressing C-x prints: control is pressed, [nothing], x is released, control is released.
In my QWidget, I use a QVBoxLayout to which I put two QWebEngine views. I tried to override the key press method of the QWebEngineView, but they don't seem to catch anything (and this is the behaviour I expected).
def __init__(self):
self.qtwindow = KeyCaptureWidget()
self.layout = QVBoxLayout()
self.view1 = QWebEngineView() # or a custom one to override keyPressEvent
# self.view2 = the same
self.layout.addWidget(self.view1)
self.layout.addWidget(self.view2)
self.qtwindow.setLayout(self.layout)
self.qtwindow.show()
I tried to catch these with event filters on my QWidget class but again, no luck: I see only a key release for ESC or BACKSPACE.
def __init__(self):
super().__init__()
self.installEventFilter(self)
def eventFilter(self, source, event):
logging.info("event filter: {}".format(event))
if event.type() == QEvent.KeyPress:
logging.info(" event filter key PRESS")
if event.type() == QEvent.KeyRelease:
logging.info(" event filter key RELEASE")
return True
How can I catch them ? Thank you.
Events do not necessarily propagate among all widgets, if a widget consumes it then it will no longer propagate to the parent. In the case of the keyboard events will only be consumed first by the widget that has the focus, in your case QWebEngineView consumes them before and prevents it from being projected in other widgets. If you want to hear events from the keyboard of a window then you must use the QShortcuts, and for that you must create a QShortcut:
Ctrl + C: QtGui.QKeySequence("Ctrl+C")
Ctrl + X: QtGui.QKeySequence("Ctrl+X")
Esc: QtGui.QKeySequence("Escape")
Backspace: QtGui.QKeySequence("Backspace")
Considering the above, the solution is:
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.m_view1 = QtWebEngineWidgets.QWebEngineView()
self.m_view2 = QtWebEngineWidgets.QWebEngineView()
self.m_view1.load(QtCore.QUrl("https://stackoverflow.com/questions/56890831"))
self.m_view2.load(QtCore.QUrl("https://doc.qt.io/"))
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.m_view1)
lay.addWidget(self.m_view2)
QtWidgets.QShortcut(
QtGui.QKeySequence("Ctrl+C"), self, activated=self.on_Ctrl_C
)
QtWidgets.QShortcut(
QtGui.QKeySequence("Ctrl+X"), self, activated=self.on_Ctrl_X
)
QtWidgets.QShortcut(
QtGui.QKeySequence("Escape"), self, activated=self.on_Escape
)
QtWidgets.QShortcut(
QtGui.QKeySequence("Backspace"), self, activated=self.on_Backspace
)
#QtCore.pyqtSlot()
def on_Ctrl_C(self):
print("Ctrl+C")
#QtCore.pyqtSlot()
def on_Ctrl_X(self):
print("Ctrl+X")
#QtCore.pyqtSlot()
def on_Escape(self):
print("Escape")
#QtCore.pyqtSlot()
def on_Backspace(self):
print("Backspace")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
I have an QLineEdit that will take the value of an RFID tag and the log the user in with the value that is received, I have setup the QLineEdit so that it calls a login function when the enter key is pressed.
The only issue that I am left with is that the QLineEdit is visible which is not necessary as the user will not be typing the value of their RFID tag, they will just scan it and the scanner will enter the value and press enter.
rfid_enter = QLineEdit()
rfid_enter.returnPressed.connect(lambda: log_user_in(rfid_enter.text()))
def log_user_in(value):
print(value) (THIS WILL LOG THE USER IN)
QLineEdit needs to have the focus to get the keyboard events, but to have the focus it needs to be visible, so hiding it will not be the solution.
As pointed out by the OP in the comments in the window there are only: two labels, and a few spacers that do not handle the keyboard event so there are no widgets that intercept that event so the window can get them without problems (if there are other widget like QLineEdits, QTextEdit, QSpinBox, etc. the logic could change).
Considering the above, I have implemented the following logic:
import string
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
returnPressed = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(
QtWidgets.QLabel("My Label", alignment=QtCore.Qt.AlignHCenter),
alignment=QtCore.Qt.AlignTop,
)
self.m_text = ""
self.returnPressed.connect(self.log_user_in)
def keyPressEvent(self, event):
if event.text() in string.ascii_letters + string.digits:
self.m_text += event.text()
if event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
self.returnPressed.emit(self.m_text)
# clear text
self.m_text = ""
super(Widget, self).keyPressEvent(event)
#QtCore.pyqtSlot(str)
def log_user_in(self, text):
print(text)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(240, 320)
w.show()
sys.exit(app.exec_())
The answer from eyllanesc would work if the entire window was the class that he had created, but in my case the layout will change and therefore I could not use that as the main window.
I went for a cheat approach of just trying to hide the box as much as possible and ended up with this result.
class LogInRFIDListener(QtWidgets.QPlainTextEdit):
def __init__(self):
super(LogInRFIDListener, self).__init__()
self.setTextInteractionFlags(QtCore.Qt.TextEditable)
self.setCursor(QtCore.Qt.ArrowCursor)
self.setStyleSheet("border: none; color: transparent;") # Hide the border around the text box
self.setCursorWidth(0) # Hide the cursor
def keyPressEvent(self, event): # Major restricting needed
if event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
log_user_in(self.toPlainText())
super(LogInRFIDListener, self).keyPressEvent(event)
I am working on a widget that needs to update itself when another widget it is matched with moves or is resized. Currently, I have the other widget do its own resizeEvent() and moveEvent() and inside that it emits a signal that my widget connects to.
However, I do not like this setup. Say later on I want to have my other widget do a different thing with its resizeEvent().
Is there a way for widget A (from widget A only) to be informed when widget B's resizeEvent() or moveEvent() is fired?
You could create a Helper class that is responsible for monitoring, so you do not need to overwrite the classes.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class Helper(QObject):
def setWidgets(self, emmiter, receiver):
emmiter.installEventFilter(self)
self.emmiter = emmiter
self.receiver = receiver
def eventFilter(self, obj, event):
if obj == self.emmiter:
if event.type() == QEvent.Resize:
self.receiver.resize(self.emmiter.size())
elif event.type() == QEvent.Move:
self.receiver.move(event.pos())
return QObject.eventFilter(self, obj, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
helper = Helper()
w1 = QWidget()
w1.setWindowTitle("emmiter")
w2 = QWidget()
helper.setWidgets(w1, w2)
w1.show()
w2.show()
sys.exit(app.exec_())