Variables contained in a QToolTip not updating automatically - python

I have a QToolTip on a QLineEdit and the tooltip contains variables in the text. The tooltip code is contained in the init. The problem is that the variable values in the tooltip do not update automatically when they are changed in the operation of the program. For example, I hover over the line edit and values appear in the tooltip. I change the program, go back to the line edit, and variables in the tooltip have not changed.
I can fix the issue by moving the .setToolTip to a function and calling the function EACH time ANYTHING is changed in the program, but that seems like overkill, especially when 99% of the program changes have nothing to do with this particular tooltip).
Are variables supposed to update automatically? Here is the tooltip setup code contained in the init.
self.ui.YourSSAmount.setToolTip(
'<span>Click Reports/Social Security to see your<br>SS income at each start age'
'<br><br>Your inf adj FRA amt at age {}: ${:,.0f}'
'<br>Age adjustment: {:.0f}%'
'<br>SS Income at age {}: ${:,.0f}</span>'.format(
self.generator.YouSSStartAge, self.generator.your_new_FRA_amt,
self.generator.SS66.get(self.generator.YouSSStartAge, 1.32) * 100, self.generator.YouSSStartAge,
self.generator.YourSSAmount))

The setToolTip method takes the text and stores it, and is not notified if any of the variables used to form the text change.
Given this there are 2 possible solutions:
Update the tooltip every time a variable changes:
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.le = QtWidgets.QLineEdit()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.le)
self.foo = QtCore.QDateTime.currentDateTime().toString()
self.update_tooltip()
timer = QtCore.QTimer(self, timeout=self.on_timeout)
timer.start()
def on_timeout(self):
self.foo = QtCore.QDateTime.currentDateTime().toString()
# every time any variable used to build the tooltip changes
# then the text of the tooltip must be updated
self.update_tooltip()
def update_tooltip(self):
# update tooltip text
self.setToolTip("dt: {}".format(self.foo))
if __name__ == "__main__":
app = QtWidgets.QApplication([])
w = Widget()
w.show()
app.exec_()
Override the toolTip to take the text using the variables:
from PyQt5 import QtCore, QtWidgets
class LineEdit(QtWidgets.QLineEdit):
def __init__(self, parent=None):
super().__init__(parent)
self._foo = ""
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, foo):
self._foo = foo
def event(self, e):
if e.type() == QtCore.QEvent.ToolTip:
text = "dt:{}".format(self.foo)
QtWidgets.QToolTip.showText(e.globalPos(), text, self, QtCore.QRect(), -1)
e.accept()
return True
return super().event(e)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.le = LineEdit()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.le)
self.le.foo = QtCore.QDateTime.currentDateTime().toString()
timer = QtCore.QTimer(self, timeout=self.on_timeout)
timer.start()
def on_timeout(self):
self.le.foo = QtCore.QDateTime.currentDateTime().toString()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
w = Widget()
w.show()
app.exec_()

Related

PyQt6: How can I fetch position of mouse pointer from QGraphicsScene and use the variables in the other class?

I'm new to pyqt6 and even to python. As the title indicates, I got stuck with handling mouse position variables. I was expecting to show coodinates of mouse pointer in the QLabel correspond to the mouse movement. I was able to fetch coordinates from mouseMoveEvent in the QGraphicsScene class to getPosition in the Window class. But, that coodinates couldn't be passed to the other function in Window class.
Here is my code so far.
import sys
from PyQt6 import QtWidgets
from PyQt6.QtWidgets import QVBoxLayout, QWidget, QLabel
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
def mouseMoveEvent(self, event):
self.posX = event.scenePos().x()
Window.getPosition(Window, event.scenePos().x())
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setMouseTracking(True)
scene = GraphicsScene(self)
self.setScene(scene)
class Window(QWidget):
def __init__(self):
super().__init__()
self.Layout = QVBoxLayout()
self.gw = GraphicsView() # an image is meant to be set here.
self.Layout.addWidget(self.gw)
self.label = QLabel("Coordinate: x") # wanna show coorinates here correspond to the mouse movement.
self.Layout.addWidget(self.label)
self.setLayout(self.Layout)
def getPosition(self, posX):
self.label.setText("Coordinate: x" + str(self.posX))
self.repaint()
print(posX)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec())
and got an ERROR like this:
AttributeError: type object 'Window' has no attribute 'label'
It seemas the self in the getPosition function is set to GraphicsScene class after being called (unexpectedly for me). And I have no idea this approach works or not after reading several web documents and asking help for chatGPT. Possibly, I took wrong approach to layout of Widgets.
Any suggestion would be helpful because I'm stuck whole this week with dipression.
Thanks.
Using class Window as parameter to setPosition was wrong, one needs to use an instance of this class. I did this by climbing up the parent() methods and there may be prettier ways to achieve the same. However, it works for now and I did not want to throw in too many changes.
I marked notable changes with comments.
#!/usr/bin/env python3
import sys
from PyQt6 import QtWidgets
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
def mouseMoveEvent(self, event):
self.posX = event.scenePos().x()
self.parent().parent().setPosition(event.scenePos().x()) # <-- crawl up the ancestry
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setMouseTracking(True)
scene = GraphicsScene(self)
self.setScene(scene)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.Layout = QtWidgets.QVBoxLayout()
self.gw = GraphicsView(self) # <-- pass self here
self.Layout.addWidget(self.gw)
self.label = QtWidgets.QLabel("Coordinate: x") # wanna show coorinates here correspond to the mouse movement.
self.Layout.addWidget(self.label)
self.setLayout(self.Layout)
def setPosition(self, posX): # <-- this is a setter, not a getter
self.label.setText("Coordinate: x" + str(posX)) # <-- use argument posX
self.repaint()
print(posX)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec())

How to get the return value of mousePressEvent of other widget in PyQt5

How can I get the value returned from mousePressEvent of a widget and apply it to another widget?
Here's the widget with the mousePressEvent:
class Text(QTextEdit):
...
def mousePressEvent(self, event):
if event.button()==Qt.LeftButton:
return "test"
Now I want to use the string returned from the event and apply it to another widget:
class OtherWidget(QWidget):
...
self.label=QLabel()
self.label.setText(???) # <=== How to put the string here?
...
How can I do that? I have tried the following but it does not work.
self.label.setText(Text().mousePressEvent())
The events do not return anything for what you point out is impossible, Qt to send information asynchronously uses the signals, in the next part I show an example:
from PyQt5 import QtCore, QtWidgets
class Text(QtWidgets.QTextEdit):
mousePressSignal = QtCore.pyqtSignal(str)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
text = "test: {}-{}".format(event.pos().x(), event.pos().y())
self.mousePressSignal.emit(text)
class OtherWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(OtherWidget, self).__init__(parent)
self.label = QtWidgets.QLabel()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
#QtCore.pyqtSlot(str)
def setText(self, text):
self.label.setText(text)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
t = Text()
o = OtherWidget()
o.resize(640, 480)
t.mousePressSignal.connect(o.setText)
t.show()
o.show()
sys.exit(app.exec_())

What kind of signal is emitted when QScrollArea entry is selected/clicked?

I'm having though time figuring out what kind of signal is emitted in following situation:
Basicly that's QScrollArea that holds multiple QTableWidgets:
class ScrollArea(QtGui.QScrollArea):
def __init__(self):
super(ScrollArea, self).__init__()
self.scroll_widget = QtGui.QWidget()
self.scroll_layout = QtGui.QVBoxLayout()
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setWidgetResizable(True)
self.__create_content()
self.setWidget(self._content_widget)
self.scroll_layout.addWidget(self)
self.scroll_widget.setLayout(self.scroll_layout)
def __create_content(self):
self._content_widget = QtGui.QWidget()
self._content_widget_layout = QtGui.QVBoxLayout()
self._content_widget.setLayout(self._content_widget_layout)
def add_item(self, item):
self._content_widget_layout.addWidget(item)
I'm using Plastique style for QApplication. As it can be seen from the above picture, when an item is clicked inside QScrollArea, blue border appears. What I would like to know is which signal is emitted when the border is drawn? I need this information so I can append a row to the selected QTableWidget whenever a button (on the left side) is clicked.
Also you can see that there is a 'x' inside each table, when 'x' is pressed that QTableWidget gets removed from QScrollArea. If there is a solution for previous problem, I could also remove QTableWidget depending on user selection rather than user clicking the 'x'.
To get the widget that has the focus you can use the focusChanged signal of QApplication:
from PyQt4 import QtCore, QtGui
class HorizontalHeader(QtGui.QHeaderView):
def __init__(self, parent=None):
super(HorizontalHeader, self).__init__(QtCore.Qt.Horizontal, parent)
self.button = QtGui.QToolButton(self, text="x")
self.sectionResized.connect(self.handleSectionResized)
def handleSectionResized(self):
last_ix = self.count() - 1
pos = QtCore.QPoint(self.sectionViewportPosition(last_ix) + self.sectionSize(last_ix) , 0)
self.button.move(pos)
def showEvent(self, event):
self.handleSectionResized()
super(HorizontalHeader, self).showEvent(event)
class TableView(QtGui.QTableView):
def __init__(self, *args, **kwargs):
super(TableView, self).__init__(*args, **kwargs)
header = HorizontalHeader(self)
header.button.clicked.connect(self.deleteLater)
self.setHorizontalHeader(header)
QtGui.qApp.focusChanged.connect(self.onFocusChanged)
def onFocusChanged(self, old, new):
if new == self:
self.deleteLater()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
scrollArea = QtGui.QScrollArea()
scrollArea.setWidgetResizable(True)
widget = QtGui.QWidget()
scrollArea.setWidget(widget)
lay = QtGui.QVBoxLayout(widget)
for i in range(10):
w = TableView()
model = QtGui.QStandardItemModel(4, 2, w)
w.setModel(model)
lay.addWidget(w)
scrollArea.show()
sys.exit(app.exec_())

Inactive subwindows in PyQt4

I have been struggling with a problem recently and I cannot get around it. I have a PyQt QMainWindow which contains a subwindow :
As you can figure out, clicking on the GO! button will open a number of subwindows specified by the number in the QLineEdit :
And clicking on the QCheckBox inside each subwindow should display a text.
The problem is that this works only for the last spawned subwindow. The others appear to be inactive.
Is their a way to make them active?
Please find my code below:
from PyQt4 import QtGui
import mainWin
import subWin
import sys
class MainWindowGui():
def __init__(self):
self.w = QtGui.QMainWindow()
self.MainWindow = myWinCls(self)
self.MainWindow.setupUi(self.w)
self.w.showMaximized()
class myWinCls(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self)
self.parent = parent
def setupUi(self,Widget):
self.ui = mainWin.Ui_MainWindow()
self.ui.setupUi(Widget)
self.ui.mdiArea.addSubWindow(self.ui.subwindow)
self.ui.goBtn.clicked.connect(self.show_wins)
def show_wins(self):
N = int(self.ui.nbrEdit.text())
for self.k in xrange(N):
self.show_subwins()
def show_subwins(self):
self.win = QtGui.QWidget()
self.child_window = showSubWinCls(self)
self.child_window.setupUi(self.win)
self.subwin = self.ui.mdiArea.addSubWindow(self.win)
self.win.setWindowTitle("Subwin " + str(self.k))
self.subwin.show()
class showSubWinCls(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self)
self.parent = parent
def setupUi(self, Widget):
self.ui = subWin.Ui_Form()
self.ui.setupUi(Widget)
self.ui.checkBox.clicked.connect(self.show_msg)
def show_msg(self):
if self.ui.checkBox.isChecked():
self.ui.lineEdit.setText("Yiiiiiihaaaaaa !!!")
else:
self.ui.lineEdit.setText("")
def main():
app = QtGui.QApplication(sys.argv)
app.setStyle(QtGui.QStyleFactory.create('WindowsVista'))
ex = MainWindowGui()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I am sure this problem is somehow a classic trick but despite searching for some time now, I cannot figure it out.
Thanks for your help!
The problematic part:
def show_wins(self):
N = int(self.ui.nbrEdit.text())
for self.k in xrange(N):
self.show_subwins()
def show_subwins(self):
self.win = QtGui.QWidget()
self.child_window = showSubWinCls(self) #erase refererence to previous one
self.child_window.setupUi(self.win)
self.subwin = self.ui.mdiArea.addSubWindow(self.win)
self.win.setWindowTitle("Subwin " + str(self.k))
self.subwin.show()
You are only keeping reference to one subwindow in self.child_window, the last window openned.
In show_wins, you call show_subwin N time. Each time, you redefine self.child_window as a new instance of the class showSubWinCls. You lose the reference to the previous one.
You need to keep a reference to all the windows, otherwise signals and slots won't work. You can do something like this:
class myWinCls(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self)
self.parent = parent
self.subWindowList=[]
def show_subwins(self):
...
child_window = showSubWinCls(self)
child_window.setupUi(self.win)
self.subWindowList.append(child_window)

How to override isclick() sigal in Pyqt?

I have a class BooleanButton contains extra boolean flag to toggle every click. After each click, I want it to emit a signal and the slot will receive the boolean flag. I just write the following, but of course, it won't work.
class BooleanButton(QPushButton):
def __init__(self, name):
QPushButton.__init__(self, name)
self.bool = False
def clicked(self, bool):
self.bool = not self.bool
self.emit(self.bool)
After creating the object, it connects to a slot. When I click this button, a swapping true-false signal will send to the slot.
bool_btn.isclicked[bool].connect(widget.func)
Thanks.
First, don't call a method clicked, that will hide the buttons clicked signal.
If you want to define a new signal, you need to do so using QtCore.pyqtSignal, then you can connect the clicked singal to a slot that will in turn emit your custom signal. Example:
class BooleanButton(QPushButton):
isclicked = pyqtSignal(bool)
def __init__(self, name):
QPushButton.__init__(self, name)
self.bool = False
self.clicked.connect(self.on_clicked)
def on_clicked(self, bool):
self.bool = not self.bool
self.isclicked.emit(self.bool)
As three_pineapples said, QPushButton comes with this feature built-in. Here's a simple example illustrating this behaviour.
from PyQt4 import QtGui, QtCore
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.button = QtGui.QPushButton("Click me", self)
self.button.setCheckable(True)
self.lineEdit = QtGui.QLineEdit(self)
self.button.clicked.connect(self.onClicked)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.lineEdit)
def onClicked(self, checked):
if checked:
self.lineEdit.setText("Button checked")
else:
self.lineEdit.setText("Button unchecked")
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
So your BooleanButton is actually just a QPushButton.

Categories

Resources