PyQt: Why does new window close immediately after opening it [duplicate] - python

This question already has answers here:
PyQt window closes immediately after opening
(3 answers)
Closed 12 months ago.
I have a main window and I want to open a another window (not a dialog) on button press. My problem is that the new window closes almost immediately after it opens. I have read the available articles, and tried to implement the solutions, but seem to have no luck. This is my entire code:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MainWindow (QMainWindow):
def __init__(self):
win = QWidget()
win.adjustSize()
grid=QGridLayout()
grid.setRowStretch(0, 1)
grid.setRowStretch(1, 1)
grid.setRowStretch(5, 1)
for i in range(0,5):
for j in range(0,4):
if i==0 and j==2:
l1=grid.addWidget(QLabel("Choose an option:"),i,j, 2, 2)
if i==2 and j==1:
b1= QPushButton("Get Best Match")
grid.addWidget(b1,i,j)
elif i==2 and j==2:
b2=QPushButton("Button2")
grid.addWidget(b2,i,j)
elif i==2 and j==3:
b3=QPushButton("Button3")
grid.addWidget(b3,i,j)
b5=grid.addWidget(QLabel(""),3,4)
b4=QPushButton("Button4")
grid.addWidget(b4,2,4)
w1=b1.clicked.connect(window1)
b2.clicked.connect(Win2)
b3.clicked.connect(Win3)
b4.clicked.connect(Win4)
win.setLayout(grid)
win.setGeometry(100,100,width//2,height//2,)
win.setWindowTitle("PYQT")
win.show()
win.setStyleSheet("""
.QPushButton {
height: 30px ;
width: 20px ;
}
.QLabel {
qproperty-alignment: AlignCenter;
font-size:12pt
}
""")
sys.exit(app.exec_())
class window1():
def __init__(self, pressed):
super(window1, self).__init__()
win1 = QWidget()
win1.adjustSize()
win1.setGeometry(100,100,width//2,height//2,)
win1.setWindowTitle("Get Best Match")
win1.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
screen_resolution = app.desktop().screenGeometry()
width, height = screen_resolution.width(), screen_resolution.height()
main=MainWindow()
Could someone please help me with this? I have been stuck for some time now.

The window is disappearing because it goes out of scope at the end of your __init__ function. Since there are no further references to it, the python garbage collector removes it.
Usually PyQt objects keep references to their children so this is not a problem. Since you want the widget to open in a separate window, you can't assign it a parent, so you need to store a reference to it somewhere else. The obvious candidate is the MainWindow class.
You can make win a member of MainWindow by using self.win = QWidget() instead of win = QWidget(). The window will now stay open for the lifetime of MainWindow unless you close it.
You have other problems with your code, but this explains why the window disappears.

Related

Open popup notification on the same monitor where the mainwindow is in python an pyqt5

In my QMainWindow i have a button which opens a new QDialog in the bottom right monitorcorner with a successmessage when i click it.
Now, if i move the QMainWindow to another monitor (i have 3 monitor) and click the button the successmessage popup appears in the monitor where the QMainWindow was opened. What i want is that the popup message appears in the monitor where my QMainWindow actually is. So if i move the QMainWindow to Monitor 1 and click the button, the successpopup should opens in monitor 1. If the QMainWindow is in monitor 2, the successpopup should open in monitor 2 an same for monitor 3.
with
screenNumber = QDesktopWidget().screenNumber(self)
i can read the screennumber where the mainwindow is. and this works fine. Evertime i click the button i read out the screennumber. But i don't found a way, to set the screennumber to my notification.
Any ideas?
Edit:
maybe it helps if i show my notify class
notes.py
from UIs.UI_notify import Ui_Notification
from PyQt5.QtWidgets import QDialog, QApplication, QDesktopWidget
from PyQt5 import QtCore
from PyQt5.QtCore import QRect, QPropertyAnimation, QTimer
import sys
class icon():
checked = "check-circle"
alert = "times-circle"
question = "question-circle"
class notify(QDialog, Ui_Notification):
def __init__(self, parent=None):
super(notify,self).__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground)
## Some helping stuff
############################################################
parent_sSize = QDesktopWidget().screenGeometry(parent)
parent_screenNumber = QDesktopWidget().screenNumber(parent)
sSize = QDesktopWidget().screenGeometry()
screenNumber = QDesktopWidget().screenNumber()
print("notification ScreenNumber = " + str(screenNumber))
print(sSize.width())
print(sSize.height())
print("Parents ScreenNumber = " + str(parent_screenNumber))
print(parent_sSize.width())
print(parent_sSize.height())
self.Note_Exit.clicked.connect(self.close)
## ScreenSize from parent
############################################################
self.hidedPos = QRect(parent_sSize.width()-self.width()-10,
parent_sSize.height()-self.height()+200,
self.frameGeometry().width(),
self.frameGeometry().height())
self.showPos = QRect(parent_sSize.width()-self.width()-10,
parent_sSize.height()-self.height()-50,
self.frameGeometry().width(),
self.frameGeometry().height())
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.setGeometry(self.hidedPos)
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.anim.setEndValue(self.showPos)
self.anim.start()
self.notyTimer = QTimer()
self.notyTimer.singleShot(4000,self.hideNote)
def hideNote(self):
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.anim.setEndValue(self.hidedPos)
self.anim.start()
self.anim.finished.connect(self.close)
if __name__ == "__main__":
notes = QApplication(sys.argv)
dialog = notify()
dialog.show()
sys.exit(notes.exec())
You cannot use the size of the widget during its construction, as at that moment it has a default size (640x480 for top level widgets, 100x30 for widgets created with a parent, including dialogs): the only reliable option is to use the sizeHint() or ensure that the layout has been properly activated with adjustSize().
Then, you don't need the screen to get the target position, as the parent geometry will suffice, but you do need it for the start position, otherwise the dialog will "pop up" at an arbitrary point below the window. Note that QDesktopWidget is considered obsolete, and you should use QScreen instead.
Finally, since you might want to reuse the notification, the start position should be set when the popup is actually being shown, not before. The same goes for the position when hiding (in case the notification could be moved).
class Notify(QDialog, Ui_Notification):
def __init__(self, parent):
super().__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.showAnim = QPropertyAnimation(self, b'geometry')
self.showAnim.setDuration(700)
self.showAnim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.hideAnim = QPropertyAnimation(self, b'geometry')
self.hideAnim.setDuration(700)
self.hideAnim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.hideTimer = QTimer(self, singleShot=True)
self.hideTimer.setInterval(4000)
self.hideTimer.timeout.connect(self.hideNote)
self.showAnim.finished.connect(self.hideTimer.start)
self.hideAnim.finished.connect(self.close)
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.adjustSize() # important!
endRect = self.rect()
center = self.parent().geometry().center()
endRect.moveCenter(center)
screen = QApplication.screenAt(center)
startRect = QRect(endRect)
startRect.moveTop(screen.geometry().bottom())
self.setGeometry(startRect)
self.showAnim.setStartValue(startRect)
self.showAnim.setEndValue(endRect)
self.showAnim.start()
self.show()
def hideNote(self):
rect = self.geometry()
self.hideAnim.setStartValue(rect)
screen = QApplication.screenAt(rect.center())
rect.moveTop(screen.geometry().bottom())
self.hideAnim.setEndValue(rect)
self.hideAnim.start()
if __name__ == "__main__":
notes = QApplication(sys.argv)
notes.setStyle('fusion')
w = QMainWindow()
b = QPushButton('click!')
w.setCentralWidget(b)
w.show()
notify = Notify(w)
b.clicked.connect(lambda: notify.setNote())
sys.exit(notes.exec())
Be aware that if you're going to create the popup every time, you should also set the WA_DeleteOnClose attribute in order to destroy it when closed, otherwise it will be kept in memory.
Note: QTimer.singleShot() is a static function, creating an instance of QTimer to use it is pointless, as that instance won't be used and a new QTimer would be created anyway.
Thank you.
In the meantime i found a solution which works for me.
Look a little dirty but works.
could you tell me, whats the difference between my code an yours?
Why should i use your code, except the fact that you are a better programmer ;-)
I set the WA_DeleteOnClose Attribute.
Good to know that. Thanks
notes.py
from UIs.UI_notify import Ui_Notification
from PyQt5.QtWidgets import QDialog, QApplication
from PyQt5 import QtCore
from PyQt5.QtCore import QRect, QPropertyAnimation, QTimer
import sys
class icon():
checked = "check-circle"
alert = "times-circle"
question = "question-circle"
clock = "clock"
class notify(QDialog, Ui_Notification):
def __init__(self, parent=None):
super(notify,self).__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
self.setWindowModality(QtCore.Qt.NonModal)
self.Note_Exit.clicked.connect(self.close)
self.parent_h = self.parent().geometry().height()
self.parent_w = self.parent().geometry().width()
self.parent_x = self.parent().geometry().x()
self.parent_y = self.parent().geometry().y()
self.dialog_w = self.width()
self.dialog_h = self.height()
self.setGeometry(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h+120, self.dialog_w, self.dialog_h)
## ScreenSize from parent
############################################################
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.anim.setEndValue(QRect(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h-20, self.dialog_w, self.dialog_h))
self.anim.start()
self.notyTimer = QTimer()
self.notyTimer.singleShot(4000,self.hideNote)
def hideNote(self):
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.anim.setEndValue(QRect(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h+120, self.dialog_w, self.dialog_h))
self.anim.start()
self.anim.finished.connect(self.close)
if __name__ == "__main__":
notes = QApplication(sys.argv)
dialog = notify()
dialog.show()
sys.exit(notes.exec())

How to make standard threading run a function and populate a widget in Python/PyQt5 [duplicate]

This question already has answers here:
Qt - updating main window with second thread
(3 answers)
How do I create a Window in different QT threads?
(4 answers)
Closed 1 year ago.
I am trying to messing up with something and I am not able to create a widget using threading.
Could someone take a look at my test script and let me know why this does not work?
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5 import *
import threading
import time
class Main(QMainWindow):
def __init__(self):
super(Main, self).__init__()
self._build_ui()
main_thread = threading.Thread(target=self.thread_function)
main_thread.start()
def thread_function(self):
i = 0
while True:
message = "Message number: {}".format(i)
print (message)
self.create_message_widget(message)
i+=1
time.sleep(1)
def create_message_widget(self, message):
print ("Testing 3")
self.label = QLabel(self)
self.label.setText(message)
self.verticalLayout.addWidget(self.label)
def _build_ui(self):
self.setWindowTitle("Testing Window")
# set the central widget and main layout
self.centralWidget = QWidget(self)
self.verticalLayout = QVBoxLayout(self)
self.setCentralWidget(self.centralWidget)
self.centralWidget.setLayout(self.verticalLayout)
self.button = QPushButton("Run")
self.button.clicked.connect(lambda: self.create_message_widget("Testing 1"))
# adding widgets to the window
self.verticalLayout.addWidget(self.button)
self.create_message_widget("Testing 2")
self.create_message_widget("Testing 2")
self.create_message_widget("Testing 2")
# show main window
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Main()
sys.exit(app.exec())
The following script shows 3 functions where I did few Testing X.
You can notice the Testing 2 are created fine.
When we click the button we can see Testing 1 which is populated by button press.
In thread_function I call create_message_widget function and I thought that should automatically create widget every 1 sec but it does not do anything. Could someone explain why and if this is possible to make it works?

What's a good way to achieve fast movement of an image following the cursor?

I've been working on a chess GUI for a short while. Am new to PyQt5. Already coded quite a bit though, so for the sake of generality (and to keep the lines of code here to a minimum), I'll just treat it as any board game that has a grid, and has pieces navigating the grid. I managed to get drag and drop behaviour in different ways, but they all had some (for me significant) downside. In any case, the particular interaction I am trying to implement is 'grabbing' a piece from a certain square (on click) and then having it follow the mouse, until it is dropped onto a different square (on release).
Version info
PyQt5 version: 5.15.0
Windows version: Windows 10 Pro 64 bits (10.0)
First Approach
Initially I was hoping simply moving a QLabel with a given pixmap might do the trick but when moving the mouse quite fast, it seems the drawing is not fast enough to keep up with the movement of the mouse. As a result, it looks like the image phases in and out of existence, or only half of it renders before it gets redrawn. Tried something similar using QGraphicsView and QGraphicsScene, and some QGraphicsItem objects and making them movable. But the results were the same, image cutting off while trying to keep up with the cursor. Example code attempting to use QGraphicsView:
from PyQt5.QtWidgets import (QMainWindow, QGraphicsView, QApplication, QGraphicsPixmapItem,
QGraphicsScene, QGraphicsItem)
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt
import sys
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.pm = QPixmap(70,70)
self.pm.fill(Qt.black)
self.item = QGraphicsPixmapItem(self.pm)
self.setGeometry(100,100,900,900)
self.scene = QGraphicsScene()
self.view = QGraphicsView(self.scene, self)
self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.viewWidth = 800
self.viewHeight = self.viewWidth
self.view.setGeometry(0,0,self.viewWidth, self.viewHeight)
self.bk = self.scene.addPixmap(self.pm)
self.bk.setFlag(QGraphicsItem.ItemIsMovable)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
With this code, if you grab the 'piece' and go back and forth with the mouse at a high speed, I mainly see half/a quarter of the image. In the middle of the area I move the object, I usually don't see the object at all. Video of this in action: https://vimeo.com/user41790219
Second Approach
I found the QDrag class, and with it, a reasonable way of creating draggable pieces. It looks fantastic, absolutely no problems once the drag actually started, the image moves very smoothly. However, I wanted the drag to start on a mousePressEvent, and have it start immediately. With 'start immediately', I rather mean render the pixmap at the mouse location on click. This does not occur on Windows. Now, as stated in https://doc.qt.io/qt-5/qdrag.html#exec, the QDrag.exec_() is blocking on Windows. It says that once entering exec, it will call processEvents() frequently to make sure the GUI stays responsive. This is all fine, but on Windows it does not render the image assigned to QDrag, until the mouse has moved some amount. This is a shame, because on Linux it actually looks kinda snappy (on click it moves to the mouse instantly). Maybe it's a bit petty, but I really want to have the piece instantly snap to the mouse. This is some sample code I kinda pieced together/came up using QDrag and QPushbuttons that shows this behaviour:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtSvg import *
import sys
BOARDSIZE = 500
SQUARESIZE = 100
PIECESIZE = 70
FILES = 5
RANKS = 5
class Square(QPushButton):
def __init__(self, pixmap=None, size=100, dark=False):
super(Square,self).__init__()
self.pixmap = pixmap
self.size = size
if dark:
self.bg = '#8CA2AD'
else:
self.bg = '#DEE3E6'
self.setFixedWidth(size)
self.setFixedHeight(size)
if self.pixmap is not None:
self.setIcon(QIcon(self.pixmap))
self.setIconSize(self.pixmap.rect().size())
self.setStyle()
self.setAcceptDrops(True)
def setStyle(self):
self.setStyleSheet(f"""
QPushButton {{
background-color:{self.bg};
color:#ffffff;
border-width:0px;
border-radius: 0px;
padding:0px 0px 0px 0px;
}}
""")
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
if self.pixmap is not None:
s = self.pixmap.width()
drag = QDrag(self)
drag.setPixmap(self.pixmap)
mime = QMimeData()
mime.setText('piece')
drag.setMimeData(mime)
drag.setHotSpot(QPoint(int(s/2) , int(s/2)))
self.setIcon(QIcon())
self.pixmap = None
drag.exec_(Qt.MoveAction | Qt.CopyAction)
def dragEnterEvent(self, event):
event.accept()
def dropEvent(self, event):
pixmap = QPixmap(PIECESIZE, PIECESIZE)
pixmap.fill(Qt.black)
self.pixmap = pixmap
self.setIcon(QIcon(pixmap))
self.setIconSize(pixmap.rect().size())
class Board(QWidget):
def __init__(self, parent=None, width=BOARDSIZE, height=BOARDSIZE):
super(Board,self).__init__(parent)
self.width = width
self.height = height
self.setFixedWidth(self.width)
self.setFixedHeight(self.height)
grid = QGridLayout(self)
grid.setSpacing(0)
self.setAcceptDrops(False)
size = int(self.width/FILES)
pixmap = QPixmap(PIECESIZE, PIECESIZE)
pixmap.fill(Qt.black)
dark = True
for row in range(0,FILES):
for col in range(0,RANKS):
if row == 0 or row == 4:
pm = pixmap
else:
pm = None
square = Square(pixmap=pm,size=size,dark=dark)
grid.addWidget(square, row, col)
dark = not dark
self.setLayout(grid)
self.setAcceptDrops(False)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.board = Board(parent=self)
self.setGeometry(100,100,BOARDSIZE+50,BOARDSIZE+50)
self.setCentralWidget(self.board)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
But in this example, when you click, the drag only visually initiates after the mouse has been moved, I would want it to visually initiate immediately after the mouse is clicked. This is Windows-specific.
Mention-worthy stuff
I found that on a QApplication, you can set the startDragTime and startDragDistance, but after setting those to some values (including 0 and -1), nothing happened, so I assumed they are just symbolic variables to be used as globals.
Question
What I'm looking for is the QDrag behaviour after the mouse moves slightly, i.e. the smooth movement during the drag + that it shows up immediately after the mousePressEvent. I'm also fine with any anything involving the use of QObjects with a pixmap and simply moving them, as long as the movement under the cursor looks smooth (doesnt clip or cut off). Any hints/ideas, or resources I may have missed are greatly appreciated!

How can I add a submit, output and input box using PyQt4?

I never tried creating a GUI with a language other than Java(kinda left it aside not long ago)
and started using Python.
made a simple program that calculates Pi to a certain digit as the user wishes.
Now, I created a window with PyQt4, made a button and got everything in place.
How can I add a input box so that the user could enter a number into it, make the button "Enter" the information and at the end of all that output it to the window instead of the terminal?
That's what I've got for now:
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from decimal import *
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(50, 50, 800, 600)
self.setWindowTitle("Pi's Nth Digit")
self.setWindowIcon(QtGui.QIcon('icon.jpg'))
self.buttons()
def buttons(self):
btn = QtGui.QPushButton("Quit",self)
btn1 = QtGui.QPushButton("Get Pi",self)
btn.clicked.connect(QtCore.QCoreApplication.instance().quit)
btn1.clicked.connect(self.getpi())
btn1.resize(btn1.sizeHint())
btn.resize(btn.sizeHint())
btn1.move(350,500)
btn.move(450,500)
self.show()
def start():
app = QtGui.QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
start()
don't mind the getpi function.
Thanks! :)
You would want to use a QLineEdit or a QSpinBox for a number. If you want multiple things in a widget you would use a layout. A QMainWindow typically has one central widget and toolbars and dock widgets.
class Window(QtGui.QMainWindow):
def __init__(self):
super().__init__()
self.container = QtGui.QWidget()
self.setCentralWidget(self.container)
self.container_lay = QtGui.QVBoxLayout()
self.container.setLayout(self.container_lay)
# Input
self.le = QtGui.QLineEdit()
self.container_lay.addWidget(self.le)
# enter button
self.enter_btn = QtGui.QPushButton("Enter")
self.container_lay.addWidget(self.enter_btn)
self.enter_btn.clicked.connect(self.run) # No '()' on run you want to reference the method.
# display
self.container_lay.addWidget(QtGui.QLabel("Answer:"))
self.ans = QtGui.QLabel()
self.container_lay.addWidget(self.ans)
def run(self):
precision = self.le.text()
pi = str(round(math.pi, precision)) # probably different formatting
self.ans.setText(pi)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
You have almost everything, just add a QLineEdit to get the input and a QLabel where to show the result (with QLabel.setText).

Functions still works despite there are same variables names in the code

I came across this style of creating UI and thought I had tried it out with some radio buttons.
Basically I created 2 functions - testButtons01Group and testButtons02Group that caters to 3 radio buttons each. However I had thought I will expect some error say if I clicked onto Grp02 push button, seeing that I named all 6 radio buttons in the same conventions but yet it is still working as per normal.
Why is this so?
Also, just wondering in this code style i wrote, is it feasible, or will it cause any confusions?
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class mainWindow(QDialog):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
grid = QGridLayout()
grid.addWidget(self.testButtons01Group(), 1, 0)
grid.addWidget(self.testButtons02Group(), 2, 0)
self.setLayout(grid)
self.setWindowTitle("Radio Buttons Test")
self.resize(480, 200)
def testButtons01Group(self):
groupBox = QGroupBox("Group 01")
self.testBtn01 = QPushButton('Grp01')
self.radioBtn1 = QRadioButton("Button01")
self.radioBtn2 = QRadioButton("Button02")
self.radioBtn3 = QRadioButton("Button03")
self.radioBtn1.setChecked(True)
hbox = QHBoxLayout()
hbox.addWidget(self.radioBtn1)
hbox.addWidget(self.radioBtn2)
hbox.addWidget(self.radioBtn3)
vbox = QVBoxLayout()
vbox.addWidget(self.testBtn01)
vbox.addLayout(hbox)
vbox.addStretch(1)
groupBox.setLayout(vbox)
# Connections
self.connect(self.testBtn01, SIGNAL('clicked()'), self.testBtn01_pushBtn)
return groupBox
def testButtons02Group(self):
groupBox = QGroupBox("Group 02")
self.testBtn02 = QPushButton('Grp02')
self.radioBtn1 = QRadioButton("Button01")
self.radioBtn2 = QRadioButton("Button02")
self.radioBtn3 = QRadioButton("Button03")
self.radioBtn1.setChecked(True)
hbox = QHBoxLayout()
hbox.addWidget(self.radioBtn1)
hbox.addWidget(self.radioBtn2)
hbox.addWidget(self.radioBtn3)
vbox = QVBoxLayout()
vbox.addWidget(self.testBtn02)
vbox.addLayout(hbox)
vbox.addStretch(1)
groupBox.setLayout(vbox)
# Connections
self.connect(self.testBtn02, SIGNAL('clicked()'), self.testBtn02_pushBtn)
return groupBox
def testBtn01_pushBtn(self, *args):
print "Test Button is pushed"
self.testBtn01_Funcs()
def testBtn01_Funcs(self):
if self.radioBtn1.isChecked():
print ">>> Group01 Radio Button 1 selected"
elif self.radioBtn2.isChecked():
print ">>> Group01 Radio Button 2 selected"
else:
print ">>> Group01 Radio Button 3 selected"
def testBtn02_pushBtn(self, *args):
print "Test Button is pushed"
self.testBtn02_Funcs()
def testBtn02_Funcs(self):
if self.radioBtn1.isChecked():
print ">>> Group02 Radio Button 1 selected"
elif self.radioBtn2.isChecked():
print ">>> Group02 Radio Button 2 selected"
else:
print ">>> Group02 Radio Button 3 selected"
def main():
global app
global form
app = qApp
form = mainWindow()
form.show()
if __name__ == '__main__':
main()
Greatly appreciated for any advice
I don't have PyQt so I can't test your code, but I'd be very surprised if it really works properly. In particular, I suspect that the tests in testBtn01_Funcs() will actually give results that relate to the second button group, not the first group.
OTOH, the button creation phase should work ok - it's a similar situation as to what we discussed in this tkinter question yesterday. But for your GUI you do need separate object attributes for each button since you want to query their status after they've been created.
I'm not familiar with Qt - I normally use GTK for my GUIs, and in GTK the radiobutton callback function gets passed the radiobutton that received the signal as the first arg, so you don't need to keep references to each button if you don't want to; maybe Qt has a similar feature.
There is nothing really wrong with that style of creating GUI items, but there are some problems with how you are naming things. Giving each set of radio buttons the same attribute names means you will only be able to refer to the second set (because the first set gets over-written).
The reason why the GUI creation functions work, is because the widgets are all added to layouts. This will automatically re-parent each widget (if they don't already have a parent), so that will be enough to keep all the widgets alive.
However, one problem remains: how do you access the radio-buttons once the GUI has been created? There are several ways to do this, but one way to do it would be re-write your code as follows.
Firstly, keep references to the group-boxes that contain the buttons:
self.groupBox01 = self.testButtons01Group()
self.groupBox02 = self.testButtons02Group()
grid.addWidget(self.groupBox01, 1, 0)
grid.addWidget(self.groupBox02, 2, 0)
Secondly, keep all the widgets in the function scope when creating them, rather than setting attributes:
def testButtons01Group(self):
groupBox = QGroupBox("Group 01")
testBtn = QPushButton('Grp01')
radioBtn1 = QRadioButton("Button01")
radioBtn2 = QRadioButton("Button02")
radioBtn3 = QRadioButton("Button03")
radioBtn1.setChecked(True)
hbox = QHBoxLayout()
hbox.addWidget(radioBtn1)
hbox.addWidget(radioBtn2)
hbox.addWidget(radioBtn3)
vbox = QVBoxLayout()
vbox.addWidget(testBtn)
vbox.addLayout(hbox)
vbox.addStretch(1)
groupBox.setLayout(vbox)
testBtn.clicked.connect(self.testBtn01_pushBtn)
return groupBox
And finally, use the group-box to access the buttons you're interested in:
def testBtn01_Funcs(self):
for radioBtn in self.groupBox01.findChildren(QRadioButton):
if radioBtn.isChecked():
print ">>> Group01 %s selected" % radioBtn.text()
break
(Obviously, you will also need to do the same thing for the other button group).

Categories