PyQt "Class" object has no attribute "widget" - python

In PyQt, I have a basic program. It consists of 2 combo boxes, 1 line edit and 3 checkboxes. What I want to do is, depending on the item of the first combo box, hide / show specific widgets. However, I keep getting an error: 'ExportDialog' object has no attribute 'exportSetDelimiter_lbl'. I have defined this widget above in initUI, and I run initUIininit`, so I'm not sure why I am getting this error. Here is my code:
from PyQt5 import QtGui, QtCore, QtWidgets
import sys
class ExportDialog(QtWidgets.QMainWindow):
def __init__(self,imagePath):
super(ExportDialog, self).__init__()
self.initUI(imagePath)
#Set The GUI Position And Size
self.setGeometry(500, 500, 600, 450)
#Set The GUI Title
self.setWindowTitle("Export Deck")
#Set The GUI Icon
self.setWindowIcon(QtGui.QIcon('MainFlashcardAppIcon.png'))
def initUI(self, PATH):
#Create The New Deck Label
self.exportFormat_lbl = QtWidgets.QLabel(self)
self.exportFormat_lbl.setText("Export Format: ")
exportFormat_font = QtGui.QFont()
exportFormat_font.setPointSize(8)
self.exportFormat_lbl.setFont(exportFormat_font)
self.exportFormat_lbl.adjustSize()
self.exportFormat_combo = QtWidgets.QComboBox()
self.exportFormat_combo.setMinimumHeight(35)
self.exportFormat_combo.setFixedWidth(380)
self.exportFormat_combo.currentTextChanged.connect(self.on_combobox_changed)
self.exportDeckName_lbl = QtWidgets.QLabel(self)
self.exportDeckName_lbl.setText("Include: ")
self.exportDeckName_lbl.setFont(exportFormat_font)
self.exportDeckName_lbl.adjustSize()
self.exportDeckName_combo = QtWidgets.QComboBox()
self.exportDeckName_combo.setMinimumHeight(35)
self.exportDeckName_combo.setFixedWidth(380)
self.exportFormat_combo.addItem(".TXT")
self.exportFormat_combo.addItem(".CSV")
self.exportFormat_combo.addItem(".DB")
self.exportSetDelimiter_lbl = QtWidgets.QLabel()
self.exportSetDelimiter_lbl.setText("Set Delimiter (Leave blank for standard delimited):")
self.exportSetDelimiter_txt = QtWidgets.QLineEdit()
self.exportSetDelimiter_txt.setMaxLength(1)
self.exportSetDelimiter = QtWidgets.QLineEdit()
vboxExport_setDelimiter = QtWidgets.QVBoxLayout()
vboxExport_setDelimiter.addWidget(self.exportSetDelimiter_lbl)
vboxExport_setDelimiter.addWidget(self.exportSetDelimiter_txt)
self.includeMedia_check = QtWidgets.QCheckBox("Include HTML and Media References")
self.includeTags_check = QtWidgets.QCheckBox("Include Tags")
self.includeAllSQL_check = QtWidgets.QCheckBox("Include All SQL Tables")
self.exportFormat_combo.addItem("B3 Biology")
self.exportFormat_combo.addItem("B2 Biology")
self.exportFormat_combo.addItem("B1 Biology")
self.allComboList = ["B3 Biology", "B2 Biology", "B1 Biology"]
self.exportDeckName_combo.setCurrentIndex(self.allComboList.index(PATH))
#Create Confirm Button
self.confirmButton = QtWidgets.QPushButton(self)
self.confirmButton.setText("OK")
self.confirmButton.clicked.connect(self.createDeck)
#Create Cancel Button
self.cancelButton = QtWidgets.QPushButton(self)
self.cancelButton.setText("Cancel")
self.cancelButton.clicked.connect(self.close)
hboxExportFormat = QtWidgets.QHBoxLayout()
hboxExportFormat.addWidget(self.exportFormat_lbl)
hboxExportFormat.addStretch()
hboxExportFormat.addWidget(self.exportFormat_combo)
hboxExportName = QtWidgets.QHBoxLayout()
hboxExportName.addWidget(self.exportDeckName_lbl)
hboxExportName.addStretch()
hboxExportName.addWidget(self.exportDeckName_combo)
hboxButtonsBottom = QtWidgets.QHBoxLayout()
hboxButtonsBottom.addStretch()
hboxButtonsBottom.addWidget(self.confirmButton)
hboxButtonsBottom.addWidget(self.cancelButton)
#Create The VBoxLayout
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.addLayout(hboxExportFormat)
mainLayout.addLayout(hboxExportName)
mainLayout.addLayout(vboxExport_setDelimiter)
mainLayout.addWidget(self.includeMedia_check)
mainLayout.addWidget(self.includeTags_check)
mainLayout.addWidget(self.includeAllSQL_check)
mainLayout.addStretch()
mainLayout.addLayout(hboxButtonsBottom)
def on_combobox_changed(self, i):
if i == ".TXT":
self.exportSetDelimiter_lbl.show()
self.exportSetDelimiter_txt.show()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.hide()
elif i == ".CSV":
self.exportSetDelimiter_lbl.hide()
self.exportSetDelimiter_txt.hide()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.hide()
elif i == ".DB":
self.exportSetDelimiter_lbl.hide()
self.exportSetDelimiter_txt.hide()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.show()
def createDeck(self):
print("Exported Sucessfully")
self.close()
#Create A Windows
app = QtWidgets.QApplication(sys.argv)
window = ExportDialog("B1 Biology")
window.show()
sys.exit(app.exec_())
This is my first question, so if you need any additional information, I will add it in. Any help would be much appreciated. Thank you!

When a combobox is newly created, it has an invalid current index (-1) and no current text set. As soon as the first item is added, the index is automatically updated to 0 and the current text changes to that of the item.
You've connected to the currentTextChanged signal before adding new items, and since the function currentTextChanged assumes that the whole ui has been already created (including exportSetDelimiter_lbl), you get the attribute error.
While there's no rule for the placing of signal connections, it's usually a good habit to group all connections at the end of the function that creates them, or anyway, ensure that everything required by their connection has already been created.
So, just move the signal connection at the end of initUI and everything will work fine.
Well... No. Because you didn't set a central widget for the main window and tried to set the layout on it (which is not allowed, since a QMainWindow has a private and unaccessible layout).
Add a QWidget, call self.setCentralWidget(someWidget) and create the layout for that widget.

Related

Change the Icon of the Selected Item of the QToolBox in PyQT

Following is the PyQT code,
from PyQt5.QtWidgets import *
import sys
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QGridLayout()
self.setLayout(layout)
# Add toolbar and items
toolbox = QToolBox()
layout.addWidget(toolbox, 0, 0)
label = QLabel()
toolbox.addItem(label, "Students")
label = QLabel()
toolbox.addItem(label, "Teachers")
label = QLabel()
toolbox.addItem(label, "Directors")
app = QApplication(sys.argv)
screen = Window()
screen.show()
sys.exit(app.exec_())
What I want is whenever an Item in that ToolBox is selected, its icon should change from "arrow-straight" to "arrow-down" to represent that this item is currently opened and others are closed. Now, if another item is clicked, then the first item's arrow should again be changed back to arrow-straight and the item that is clicked gets its arrow changed now.
How can I accomplish this in PyQT? Be it from the designer or from the Code Logic.
EDIT: For example, look at this designer below,
Since the "Registration Details" is selected, so I want its arrow to be replaced with another Icon (Say "arrow down" icon). And once I select some other item in the toolbox (like View Clashes), then Registration Details' arrow should be replaced with the old arrow and View Clashes arrow should get changed to another Icon.
The code for this in Pyuic file is this,
icon = QIcon()
icon.addFile(u":/icons/icons/arrow-right.svg", QSize(), QIcon.Normal, QIcon.Off)
self.toolBox.addItem(self.page_2, icon, u"Registration Details")
You can set a default icon when adding items, and then connect the currentChanged signal in order to set the other one.
If you create a basic list with both icons, setting the proper icon is even simpler, as you only need to cycle through all items and set the icon based on the index match.
class Test(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.arrowIcons = []
for direction in ('right', 'down'):
self.arrowIcons.append(QtGui.QIcon(
':/icons/icons/arrow-{}.svg'.format(direction)))
layout = QtWidgets.QVBoxLayout(self)
self.toolBox = QtWidgets.QToolBox()
layout.addWidget(self.toolBox)
self.toolBox.currentChanged.connect(self.updateIcons)
for i in range(5):
self.toolBox.addItem(
QtWidgets.QLabel(),
self.arrowIcons[0],
'Item {}'.format(i + 1))
def updateIcons(self, index):
for i in range(self.toolBox.count()):
self.toolBox.setItemIcon(i, self.arrowIcons[index == i])

PyQt6 deleting custom widget with nested class causes the program to crash

I am using Python 3.9.5.
I have encountered some serious problem in my project and here is a minimum reproducible example code, along with some descriptions.
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
class Editor(QTextEdit):
doubleClicked = pyqtSignal(QTextEdit)
def __init__(self):
super().__init__()
self.setReadOnly(True)
def mouseDoubleClickEvent(self, e: QMouseEvent) -> None:
self.doubleClicked.emit(self)
class textcell(QGroupBox):
def __init__(self, text):
super().__init__()
self.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.label = QLabel(text)
self.apply = makebutton('Apply')
self.apply.hide()
self.editor = Editor()
self.editor.doubleClicked.connect(lambda: self.editor.setReadOnly(False))
self.editor.doubleClicked.connect(self.apply.show)
self.hbox = QHBoxLayout()
self.hbox.addSpacerItem(spacer)
self.hbox.addWidget(self.apply)
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.label)
self.vbox.addWidget(self.editor)
self.vbox.addLayout(self.hbox)
self.setLayout(self.vbox)
self.apply.clicked.connect(self.on_ApplyClick)
def on_ApplyClick(self):
self.editor.setReadOnly(True)
self.apply.hide()
def makebutton(text):
button = QPushButton()
button.setFixedSize(60, 20)
button.setText(text)
return button
class songpage(QGroupBox):
def __init__(self, texts):
super().__init__()
self.init(texts)
self.setCheckable(True)
self.setChecked(False)
def init(self, texts):
self.vbox = QVBoxLayout()
artist = textcell('Artist')
artist.editor.setText(texts[0])
album = textcell('Album')
album.editor.setText(texts[1])
title = textcell('Title')
title.editor.setText(texts[2])
self.vbox.addWidget(artist)
self.vbox.addWidget(album)
self.vbox.addWidget(title)
self.setLayout(self.vbox)
spacer = QSpacerItem(0, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
class Ui_MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(405, 720)
self.setWindowTitle('example')
frame = self.frameGeometry()
center = self.screen().availableGeometry().center()
frame.moveCenter(center)
self.move(frame.topLeft())
self.centralwidget = QWidget(self)
vbox = QVBoxLayout(self.centralwidget)
hbox = QHBoxLayout()
add = makebutton('Add')
delete = makebutton('Delete')
hbox.addWidget(add)
hbox.addSpacerItem(spacer)
hbox.addWidget(delete)
vbox.addLayout(hbox)
self.scrollArea = QScrollArea(self.centralwidget)
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.scrollArea.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
vbox.addWidget(self.scrollArea)
self.setCentralWidget(self.centralwidget)
add.clicked.connect(self.addObj)
delete.clicked.connect(self.deleteObj)
def addObj(self):
Obj = songpage(('AAA', 'BBB', 'CCC'))
self.verticalLayout.addWidget(Obj)
def deleteObj(self):
item = self.verticalLayout.itemAt(0)
widget = item.widget()
self.verticalLayout.removeItem(item)
self.verticalLayout.removeWidget(widget)
app = QApplication([])
window = Ui_MainWindow()
window.show()
app.exec()
The problem is very simple, if I click add button, the widget will be added and everything works fine, if I double click on a QTextEdit, its apply button will show and it will change from read only to editable.
After I click an apply button, the button will hide and the corresponding QTextEdit will be read only again.
I have finally managed to add a doubleClicked signal to QTextEdit.
And, the problem, is if I click delete button, instead of deleting the thing as expected, it crashes the whole application.
I am sorry the minimum reproducible example is so long, but I have only managed to reproduce the issue with all the code and I really don't know what went wrong.
So how to fix it?
Well, I have figured it out, it was caused by the spacer item that I add to every one of horizontal layouts where fixed-sized buttons are used.
All such layouts are using the same spacer item, exactly the same spacer item, not just identical.
From what I have observed, the spacer items are all references to the same object, they are all echos of the same object which lives at a fixed memory address.
I honestly don't understand how such thing works, that the same object can not only be added to multiple layouts and present in all the layouts concurrently, but also be added to the same layout multiple times, yet it always remains the original object, not duplicates of itself.
I thought when I added the same spacer item to multiple layouts, I didn't add the original spacer item, instead duplicates of the original item which are identical but are at different memory addresses, and clearly that isn't how Python works.
So when I remove a widget, everything inside it, everything inside its layout is deleted, and the spacer item is deleted.
Because all the spacer items in all the layouts are references to the original spacer item, when I delete one of the layouts, the original spacer item is deleted as well, and the spacer item is deleted from all other layouts, yet its shadows remain, and the item isn't properly removed from all the other layouts, the layouts contain references to an object that no longer exists, thus the application crashed.
By removing the definition of the spacer item and replacing adding the spacer item with .addStretch(), the bug is fixed.

Strange grafical artifacts while re-inserting widgets into gridlayout [duplicate]

I have a QVBox layout that houses a QVBox layout and a QHBox layout. I use the other QVBox layout to hold dynamically created GUI objects and the QHBox layout to hold the buttons that add/remove those objects. Everything works correctly if I position the QHBox on top of the QVBox, but when I try to position the QHBox beneath the QVBox the objects aren't removed correctly but stay there "lingering" on top of the QHBox. I'll upload pictures to demonstrate the problem. First picture is before taking action, second is after creating a new object and third is after deleting the object
Here is the code that creates and deletes the new objects
def addClient(self):
if (len(self.clients) < 5):
client = clientComponent(self)
self.clients.append(client)
index = len(self.clients)-1
self.vLayout3.addWidget(self.clients[index])
client.setIndex(index)
self.clients[index].startButton.clicked.connect(partial(self.threadcontrol, '2', client.getIndex()))
self.clients[index].stopButton.clicked.connect(partial(self.clientstop, '0', client.getIndex()))
def deleteClient(self):
if (len(self.clients) > 1):
self.vLayout3.removeWidget(self.clients.pop())
This is where I complete the layout
def initializeUi(self):
self.mainWidget = QWidget(self)
self.setCentralWidget(self.mainWidget)
self.mainLayout = QVBoxLayout(self.mainWidget)
self.hLayout1 = QHBoxLayout()
self.hLayout2 = QHBoxLayout()
self.vLayout1 = QVBoxLayout()
self.vLayout2 = QVBoxLayout()
self.vLayout3 = QVBoxLayout()
self.addServer()
self.addClient()
self.serverBox = QGroupBox('Server')
self.clientBox = QGroupBox('Client')
self.traffic1 = QLabel('0.0Mb/s', self)
self.traffic1.setAlignment(Qt.AlignRight)
self.traffic2 = QLabel('0.0Mb/s', self)
self.traffic2.setAlignment(Qt.AlignCenter)
self.traffic3 = QLabel('0.0Mb/s', self)
self.traffic3.setAlignment(Qt.AlignLeft)
self.newClientButton = QPushButton('+', self)
self.deleteClientButton = QPushButton('-', self)
self.hLayout1.addWidget(self.traffic1)
self.hLayout1.addWidget(self.traffic2)
self.hLayout1.addWidget(self.traffic3)
self.hLayout2.addWidget(self.newClientButton)
self.hLayout2.addWidget(self.deleteClientButton)
self.vLayout2.addLayout(self.vLayout3)
self.vLayout2.addLayout(self.hLayout2)
self.mainLayout.addWidget(self.plot)
self.mainLayout.addLayout(self.hLayout1)
self.serverBox.setLayout(self.vLayout1)
self.mainLayout.addWidget(self.serverBox)
self.clientBox.setLayout(self.vLayout2)
self.mainLayout.addWidget(self.clientBox)
This is happening because your main window remains the parent of the client widgets after you remove them from the layout. You will see similar behaviour if you assign a widget a parent widget without adding it to any layout.
Removing the parent should resolve the issue.
def deleteClient(self):
if (len(self.clients) > 1):
client = self.clients.pop()
self.vLayout3.removeWidget(client)
client.setParent(None)
You may also need to make a call to adjustSize to resize the window to fit the remaining widgets
When you delete a widget from layout it still remains in parent widget's
object tree, so it gets displayed outside of any layout.
To remove a widget from the object tree call widget.setParent(None).
widget.deleteLater() also works.
Here is my MCVE(Qt4, Py2.7):
from PyQt4.QtGui import (QApplication, QWidget, QPushButton,
QVBoxLayout, QHBoxLayout)
app=QApplication([])
self = QWidget()
main_layout = QVBoxLayout(self)
clients = []
l2 = QHBoxLayout()
main_layout.addLayout(l2)
b_add = QPushButton('add', self)
l2.addWidget(b_add)
def addClient():
b = QPushButton(str(len(clients)), self)
clients.append(b)
main_layout.addWidget(b)
b_add.clicked.connect(addClient)
b_rm = QPushButton('rm', self)
l2.addWidget(b_rm)
def deleteClient():
b = clients.pop()
main_layout.removeWidget(b)
# comment out two following lines to get the behavior you observe
b.setParent(None)
self.adjustSize()
b_rm.clicked.connect(deleteClient)
self.show()
app.exec_()
On my system I also have to call self.adjustSize() after deletion to resize the main window

PyQt5 dynamic creation/destruction of widgets

I have an application where upon start up the user is presented with a dialog to chose number of 'objects' required. This then generates necessary objects in the main window using a for loop (i.e. object1, object2, etc.). I want to move this selection into the main window so that this can be changed without the need to restart the application. I have no idea how to approach this as I'm not sure how to dynamically create/destroy once the application is running. Here's an example code that generates tabs in a tab widget with some elements in each tab.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class SelectionWindow(QDialog):
def __init__(self):
QDialog.__init__(self)
self.settings = QSettings('Example', 'Example')
self.numberOfTabs = QSpinBox(value = self.settings.value('numberOfTabs', type=int, defaultValue = 3), minimum = 1)
self.layout = QFormLayout(self)
self.button = QPushButton(text = 'OK', clicked = self.buttonClicked)
self.layout.addRow('Select number of tabs', self.numberOfTabs)
self.layout.addRow(self.button)
def buttonClicked(self):
self.settings.setValue('numberOfTabs', self.numberOfTabs.value())
self.accept()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.settings = QSettings('Example', 'Example')
self.tabs = self.settings.value('numberOfTabs', type = int)
self.tabWidget = QTabWidget()
for i in range(1, self.tabs + 1):
exec(('self.tab{0} = QWidget()').format(i))
exec(("self.tabWidget.addTab(self.tab{0}, 'Tab{0}')").format(i))
exec(('self.lineEdit{0} = QLineEdit()').format(i))
exec(('self.spinBox{0} = QSpinBox()').format(i))
exec(('self.checkBox{0} = QCheckBox()').format(i))
exec(('self.layout{0} = QFormLayout(self.tab{0})').format(i))
exec(("self.layout{0}.addRow('Name', self.lineEdit{0})").format(i))
exec(("self.layout{0}.addRow('Value', self.spinBox{0})").format(i))
exec(("self.layout{0}.addRow('On/Off', self.checkBox{0})").format(i))
self.setCentralWidget(self.tabWidget)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
dialog = SelectionWindow()
dialog.show()
if dialog.exec_() == SelectionWindow.Accepted:
mainwindow = MainWindow()
mainwindow.show()
sys.exit(app.exec_())
First of all, you should never use exec for things like these. Besides the security issues of using exec, it also makes your code less readable (and then much harder to debug) and hard to interact with.
A better (and more "elegant") solution is to use a common function to create tabs and, most importantly, setattr.
Also, you shouldn't use QSettings in this way, as it is mostly intended for cross-session persistent data, not to initialize an interface. For that case, you should just override the exec() method of the dialog and initialize the main window with that value as an argument.
And, even if it was the case (but I suggest you to avoid the above approach anyway), remember that to make settings persistent, at least organizationName and applicationName must be set.
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.settings = QSettings('Example', 'Example')
# this value does not need to be a persistent instance attribute
tabCount = self.settings.value('numberOfTabs', type = int)
# create a main widget for the whole interface
central = QWidget()
mainLayout = QVBoxLayout(central)
tabCountSpin = QSpinBox(minimum=1)
mainLayout.addWidget(tabCountSpin)
tabCountSpin.setValue(tabCount)
tabCountSpin.valueChanged.connect(self.tabCountChanged)
self.tabWidget = QTabWidget()
mainLayout.addWidget(self.tabWidget)
for t in range(tabCount):
self.createTab(t)
self.setCentralWidget(central)
def createTab(self, t):
t += 1
tab = QWidget()
self.tabWidget.addTab(tab, 'Tab{}'.format(t))
layout = QFormLayout(tab)
# create all the widgets
lineEdit = QLineEdit()
spinBox = QSpinBox()
checkBox = QCheckBox()
# add them to the layout
layout.addRow('Name', lineEdit)
layout.addRow('Value', spinBox)
layout.addRow('On/Off', checkBox)
# keeping a "text" reference to the widget is useful, but not for
# everything, as tab can be accessed like this:
# tab = self.tabWidget.widget(index)
# and so its layout:
# tab.layout()
setattr(tab, 'lineEdit{}'.format(t), lineEdit)
setattr(tab, 'spinBox{}'.format(t), spinBox)
setattr(tab, 'checkBox{}'.format(t), checkBox)
def tabCountChanged(self, count):
if count == self.tabWidget.count():
return
elif count < self.tabWidget.count():
while self.tabWidget.count() > count:
# note that I'm not deleting the python reference to each object;
# you should use "del" for both the tab and its children
self.tabWidget.removeTab(count)
else:
for t in range(self.tabWidget.count(), count):
self.createTab(t)

PyQt progress bar not updating or appearing until 100%

EDIT: There are a number of similar posts on PyQt4 progress bars not updating. They all focus on the issue of threads & where the program actually updates the window. Although helpful, my code was so structured that the replies were not practical. The accepted answer given here is simple, to the point & works.
I am using Python 2.7 and PyQT 4 on a Win 7 x64 machine.
I am trying to clear my window of one widget, an 'Accept' button, see code, and replace it with a progress bar.
Even though I close the 'Accept' button & add the progress bar before the processing loop is entered into. The window is only updated after the loop has finished & the progress bar jumps straight to 100%.
My code,
from PyQt4 import QtCore, QtGui
import sys
import time
class CentralWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CentralWidget, self).__init__(parent)
# set layouts
self.layout = QtGui.QVBoxLayout(self)
# Poly names
self.pNames = QtGui.QLabel("Import file name", self)
self.polyNameInput = QtGui.QLineEdit(self)
# Polytype selection
self.polyTypeName = QtGui.QLabel("Particle type", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("")
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
# Place widgets in layout
self.layout.addWidget(self.pNames)
self.layout.addWidget(self.polyNameInput)
self.layout.addWidget(self.polyTypeName)
self.layout.addWidget(polyType)
self.layout.addStretch()
# Combobox choice
def onActivated(self, text):
if text=="Random polyhedra":
self.randomPolyhedra(text)
if text=="Spheres": # not implementaed yet
self.polyTypeName.setText("Not implemented yet.")
self.polyTypeName.adjustSize()
if text=="Waterman polyhedra": # not implementaed yet
self.polyTypeName.setText("Not implemented yet.")
self.polyTypeName.adjustSize()
# New options for random polyhedra choice
def randomPolyhedra(self, text):
self.polyNumberLbl = QtGui.QLabel("How many: ", self)
self.polyNumber = QtGui.QLineEdit(self)
self.acceptSeed = QtGui.QPushButton('Accept') # Accept button created
self.acceptSeed.clicked.connect(lambda: self.ranPolyGen())
self.layout.addWidget(self.polyNumberLbl)
self.layout.addWidget(self.polyNumber)
self.layout.addWidget(self.acceptSeed) # Accept button in layout
self.randFlag = True
self.polyTypeName.setText(text)
self.polyTypeName.adjustSize()
# Act on option choices for random polyhedra
def ranPolyGen(self):
polyCount = int(self.polyNumber.text())
self.progressBar = QtGui.QProgressBar() # Progress bar created
self.progressBar.setMinimum(1)
self.progressBar.setMaximum(polyCount)
self.acceptSeed.close() # Accept button closed
self.layout.addWidget(self.progressBar) # Add progressbar to layout
for poly in range(1, polyCount+1):
time.sleep(1) # Calls to main polyhedral generating code go here
print poly
self.progressBar.setValue(poly)
self.doneLbl = QtGui.QLabel("Done", self)
self.layout.addWidget(self.doneLbl)
# Creates GUI
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
# Place central widget in layout
self.central_widget = CentralWidget(self)
self.setCentralWidget(self.central_widget)
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
self.central_widget.onActivated(text)
def main():
app = QtGui.QApplication(sys.argv)
poly = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Below is a picture of during loop execution & after completion.
I dont think I have got my head around the addWidget() method. I was under the impression that this would add another widget to the present layout (a vbox layout here) & that the .close() method removed a widget when directed to do so.
What am I missing?
You can add:
from PyQt4.QtGui import QApplication
Then in your for loop:
QApplication.processEvents()
Your app is actually becoming unresponsive, you need to call processEvents() to process the events and redraw the gui. I am not overly familiar with pyqt but I imagine another alternative is using a thread.

Categories

Resources