I have a list of dictionaries:
globalParams = [{'attr':'enabled','ctrl':'checkBoxEnabled','type':'checkBox'},
{'attr':'colorMode','ctrl':'comboBoxColorMode','type':'comboBox'}]
'ctrl' - name of the control in the Qt window.
typically, the code is as follows:
self.checkBoxEnabled.checkState()
but checkBoxEnabled is an object. and i have only a string name 'checkBoxEnabled' and cannot use it...
how to find an object by name in pyqt? something like?
self.GetObjectByName('checkBoxEnabled').checkState()
You can use QObject::findChild method. In pyqt it should be written like this:
checkbox = self.findChild(QtGui.QCheckBox, "checkBoxEnabled")
self should be a parent widget of the checkbox.
To find all the QCheckBox items in your UI you can run a loop through all the children of the widget like as below:
from PyQt5.QtWidgets import QCheckBox
from PyQt5.QtCore import QObject
class Mainwindow(QMainWindow):
def __init__(self):
# super().__init__()
self.checkbox = QCheckBox(self)
self.checkbox_1 = QCheckBox(self)
for checkstate in self.findChildren(QCheckBox):
print(f'get check state:{checkstate.checkState()}')
Related
I want to implement the following functionality into my Qt application:
User opens one or more 'input' widgets (instances of an InputWidget class), each containing a QLineEdit widget
User opens a 'helper' dialog
User selects a value in the 'helper' dialog
User presses the 'Insert' QPushButton in the 'helper' dialog
The selected value from the 'helper' dialog' is inserted into the QLineEdit of that 'input' dialog that had the last focus before the 'Insert' button was pressed
So, basically, what I want is that if the user clicks on 'Insert' in the following screenshot, the string 'Apple' should appear in the focused input dialog. The code example below does kind of work, only that the string is (usually, see below) inserted into the second one.
Here's is the code example that creates this setup:
from PyQt5.QtWidgets import (QApplication, QWidget, QHBoxLayout,
QLineEdit, QLabel, QPushButton, QComboBox)
import sys
# this is the missing bit
def determineWhichWidgetHadLastFocus():
for widget in QApplication.instance().topLevelWidgets():
if isinstance(widget, InputWidget):
# do something wonderful to determine whether this widget
# is the one that had last focus
wonderful = True
if wonderful:
return widget
return None
class BaseWidget(QWidget):
""" Base widget type """
def __init__(self, name):
super(BaseWidget, self).__init__()
self.setWindowTitle(name)
self.setupUi()
self.show()
def setupUi(self):
pass
class InputWidget(BaseWidget):
""" InputWidget contains a QLabel and a QLineEdit widget """
def setupUi(self):
self.label = QLabel("Input string:")
self.edit = QLineEdit()
layout = QHBoxLayout(self)
layout.addWidget(self.label)
layout.addWidget(self.edit)
class HelperWidget(BaseWidget):
""" HelperWidget contains a QLineEdit and a QPushButton widget. Pressing
the button inserts the content of the edit widget into the edit widget of
the last activated InputWidget """
def setupUi(self):
self.combo = QComboBox()
self.combo.addItems(["Apple", "Pear", "Banana"])
self.button = QPushButton("Insert")
self.button.clicked.connect(self.insertString)
layout = QHBoxLayout(self)
layout.addWidget(self.combo)
layout.addWidget(self.button)
def insertString(self):
widget = determineWhichWidgetHadLastFocus()
if widget:
widget.edit.insert(self.combo.currentText())
def main():
app = QApplication(sys.argv)
diag1 = InputWidget("Input dialog")
diag2 = InputWidget("Another input")
helper = HelperWidget("Helper")
app.exec_()
if __name__ == "__main__":
main()
The missing part is the determineWhichWidgetHadLastFocus() function.
This function is supposed to do something wonderful that allows it to determine which 'input' was the last to hold focus. Currently, it traverses the list of top level widgets from QApplication, but the order of top level widgets doesn't reflect the activation order (it usually, but not always appears to be the order of creation).
One idea that came to my mind was to install an event filter that keeps track of the FocusIn events. That would be easy for the InputWidget class in my example, but might not work so well for my real application that has many QLineEdits, QTextEdits and descended classes all over the place. I'd rather not go that way.
Any other ideas?
It turns out that the event filter idea is the way to go after all. What I did was first create an event filter class that emits a signal if the sending object is a QLineEdit object:
from PyQt5.QtCore import QObject, QEvent, pyqtSignal
class inputFocusFilter(QObject):
focusIn = pyqtSignal(object)
def eventFilter(self, widget, event):
if event.type() == QEvent.FocusIn and isinstance(widget, QLineEdit):
# emit a `focusIn` signal, with the widget as its argument:
self.focusIn.emit(widget)
return super(inputFocusFilter, self).eventFilter(widget, event)
This filter is installed for a custom QApplication class so that any event that is created passes the filter. The signal focusIn is connected to a setter function that remembers the last input widget that received focus:
class MyApplication(QApplication):
def __init__(self, *arg, **kwarg):
super(MyApplication, self).__init__(*arg, **kwarg)
self._input_focus_widget = None
self.event_filter = inputFocusFilter()
self.event_filter.focusIn.connect(self.setInputFocusWidget)
self.installEventFilter(self.event_filter)
def setInputFocusWidget(self, widget):
self._input_focus_widget = widget
def inputFocusWidget(self):
return self._input_focus_widget
MyApplication is used instead of QApplication in the first line of main(). Now, the call to determineWhichWidgetHadLastFocus() in HelperWidget.insertString() can be replaced by QApplication.instance().inputFocusWidget(), and everything works as intended.
This is a class which form i made in qt5 designer. The slot is called twice when I click the button.
class CustomerList(QWidget, Ui_CustomerList):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setupUi(self)
self.buttX.clicked.connect(self.on_buttX_clicked)
#pyqtSlot()
def on_buttX_clicked(self):
print("on_buttX_clicked")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = CustomerList()
w.show()
sys.exit(app.exec_())
What am I missig here?
Your button is called buttX in designer, so the "Auto connect by name" feature in setupUi() finds a matching slot.
You can either
remove the explicit connect
rename the button
rename the slot
I would personally go for the latter, i.e. use a slot name that does not have the pattern the "auto name connect" is looking for.
E.g. onButtXClicked
"I want to delete and reinstantiate a QWidget." I am writing a GUI in PyQt4 and for that I write classes which inherit from QGridLayout. These would look like this:
from PyQt4 import QtGui
class StartTab(QtGui.QGridLayout):
def __init__(self, mainWindow, tab):
super(StartTab, self).__init__(tab)
< body (whatever this tab needs) >
Here mainWindow is somewhat the framing window of the GUI to which the menu bar belongs and is instatiated when the program starts. So things like "File -> New Project" will be handled in this class. When the program starts, the tabs will be created in the __init__ of mainWindow, so like:
from PyQt4 import QtGui, QtCore
from start_tab import StartTab
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
< some variables >
self.tabWidget = QtGui.QTabWidget(self)
self.tab1 = QtGui.QWidget()
self.startTab = StartTab(self, self.tab1)
self.tabWidget.addTab(self.tab1, 'Start Tab')
< some other tabs >
In mainWindow I also have a function newProject() which is called when the user hits the above mentioned "File -> New Project" button. There is my problem. I tried reinitializing the tabs in the newProject() function so the old tabs will be deleted and new ones will be created when the user wants to start a new project from a running program. I don't know how though. What I tried is somewhat to just copy the line from the __init__ of mainWindow:
def newProject(self) :
self.startTab = StartTab(self, self.tab1)
but this leaves me with an error message:
QLayout: Attempting to add QLayout "" to QWidget "", which already has
a layout
I know I probably haven't understood the concept of this whole Qt thing yet, but I would much appreciate some help. Thanks!
How to have all attributes and Qt views of an object updated when reloading the python module that defines this object, without breaking or even touching the links between this object and other objects?
Here is an exemple:
The QMainWindow of a Qt application includes both local widgets and a reloadable widget:
from PyQt4 import QtCore, QtGui
import reloadable
name='Test reloadable'
class MyWindow(QtGui.QMainWindow):
def __init__(self):
# Initialize the main window
super(MyWindow, self).__init__()
self.setWindowTitle(name)
# Create an instance of the reloadable widget
self.reloadableWidget= reloadable.ReloadabelWidget()
# Create a local widget that can ask for and display an attribute of the reloadable widget
button1 = QtGui.QPushButton('Identify')
button1.clicked.connect(self.identifyReloadable)
self.label1=QtGui.QLabel('')
layout1 = QtGui.QHBoxLayout()
layout1.addWidget(button1)
layout1.addWidget(self.label1)
localWidget=QtGui.QWidget()
localWidget.setLayout(layout1)
# Put the local widget and the reloadable widget in the window
centralWidget=QtGui.QWidget()
layout = QtGui.QVBoxLayout()
layout.addWidget(localWidget)
layout.addWidget(self.reloadableWidget)
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
def identifyReloadable(self):
identity=self.reloadableWidget.identify()
self.label1.setText(identity)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName(name)
main = MyWindow()
main.show()
sys.exit(app.exec_())
This reloadable widget (QFrame) has a method 'identify()' called by main and a method 'reload()' called by itself. This reload method essentially reloads the class module, replace the class attribute by the possibly updated module.class, and tries to update the Qt views of the object.
# This is module reloadble.py
from PyQt4 import QtCore, QtGui
import inspect
identity='1'
color='blue'
class ReloadabelWidget(QtGui.QFrame,object):
def __init__(self):
QtGui.QFrame.__init__(self)
self.setFrameStyle(1)
self.reinit()
def reinit(self):
button = QtGui.QPushButton('Reload',self)
button.clicked.connect(self.reload)
label=QtGui.QLabel(identity)
label.setStyleSheet("QLabel { background-color : white; color : %s; }" % color);
layout = QtGui.QHBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
self.setLayout(layout)
self.setUpdatesEnabled(True)
def identify(self):
return identity # will change each time the file is edited with a new identity in line 3
def reload(self):
module=inspect.getmodule(self)
reload(module)
self.reinit() # object is reinitialized
self.__class__= module.ReloadabelWidget # callable attributes are now up to date
self.update() # naive attempt to update the Qt view => does not work
# add code here to have the GUI part of self, i.e. the QFrame updated ...
# ... whereever it is displayed.
The main application is running and the programmer redefines the method identify() and the color of a label by editing and saving the code of the reloadable module.
identity='2'
color='red'
The reload button is then clicked to run the reload() method of the reloadable widget. Its methods are properly updated and clicking the identify button displays the updated identity 2. But the widget's view is not updated because I don't know how to do it. What are the lines to be added at the end of the reload method to obtain the redisplay of the new interface, created by self.reinit()
?
PS1: For those who like to know what is the goal behind a question, I want to accelerate the development of a complicated multi-module application by having both the attributes and the gui of a module updated at code edition, without restarting the application.
PS2: This question replaces a bad one that I had asked without being sufficiently explicit. Since there was already several answers, I make this new post. I now provide a complete code entirely tested and make my point clearly.
I want to take an action when a widget was resized.
Is there a way to catch that without installing an event filter on that widget (and, obviously, without subclassing it)? AFAIK, QWidget does not have a resized signal.
You can derive from widget class and reimplement resizeEvent event
If you have any other QObject that can have strict relation to that QWidget you may use QObject::installEventFilter(QObject * filter) and overload bool eventFilter(QObject *, QEvent *). See more at Qt docs
In case you are using Python with PyQt4, you can set widget.resizeEvent to your function without sublclassing it:
#!/usr/bin/env python
import sys
from PyQt4 import QtCore, QtGui
def onResize(event):
print event
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
widget = QtGui.QPushButton('Test')
widget.resizeEvent = onResize
widget.resize(640, 480)
widget.show()
sys.exit(app.exec_())
Sorry, it looks like a hack, but I use this:
some_widget.resizeEvent = (lambda old_method: (lambda event: (self._on_resized(event), old_method(event))[-1]))(some_widget.resizeEvent)
This is a couple of years too late, but I was working on a transparent overlay widget that would completely cover the parent. You can not do what you want without subclassing, but you can restrict the subclassing to an instance as #reclosedev suggests, meaning that you don't have to actually create a subclass.
I wrote the following snippet (which works in PyQt4) for following the size of any widget that the widget is added to:
class TransparentOverlay(QtGui.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setAttribute(QtCore.Qt.WA_NoSystemBackground)
self._updateParent(self.parentWidget())
def setParent(self, parent, *args):
prevParent = self.parentWidget()
super().setParent(parent, *args)
self._updateParent(parent, prevParent)
def unsetParent(self, parent=None):
if parent is None:
parent = self.parentWidget()
if parent is not None and hasattr(parent.resizeEvent, '_original'):
parent.resizeEvent = parent.resizeEvent._original
def _updateParent(self, parent, prevParent=None):
if parent is not prevParent:
self.unsetParent(prevParent)
if parent is not None:
original = parent.resizeEvent
def resizeEventWrapper(event):
original(event)
self.resize(event.size())
resizeEventWrapper._original = original
parent.resizeEvent = resizeEventWrapper
self.resize(parent.size())
This code uses a couple of neat tricks that are possible with Python:
The original method is stashed in the _original attribute of the new one. This is possible because functions are objects.
The new method truly subclasses any QWidget instance, meaning that you do not have to create an actual subclass. Each parent instance will effectively become an instance of a subclass by virtue of the tacked on method.
If you need a one-time thing, all of the code for removing the subclassed resizeEvent method and replacing it with the original can be trashed. In that case, the solution is basically a fancier version of #reclosedev's solution, but with #Chris's comments about preserving the original addressed.
The only caveat with this code is that it does not support GL widgets correctly, so for example the overlay can not always be added to the viewport of a QGraphicsView. It can, however, be added to the QGraphicsView itself.
You can override the resizeEvent by
def resizeEvent(self, newSize):
#do code here