Building Qt Gui from few classes together - python

Below is a short example of my Gui. I am trying to split my Gui in few parts.
The elements of InputAxis should be on the same height (horizontal split) and self.recipient should be below them (vertical split).
In InputAxis I am trying to place a QLineEdit but in my Gui I don't see it.
import sys
from PySide import QtCore
from PySide import QtGui
class InputAxis(object):
def __init__(self):
self.frame = QtGui.QFrame()
self.input_interface = QtGui.QLineEdit()
self.form_layout = QtGui.QFormLayout()
def genAxis(self):
self.frame.setFrameShape(QtGui.QFrame.StyledPanel)
self.form_layout.addRow('&Input:', self.input_interface)
return self.frame
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self, parent = None)
self.layout = QtGui.QVBoxLayout()
self.form_layout = QtGui.QFormLayout()
self.axes = list()
self.axes.append(InputAxis())
self.axes.append(InputAxis())
self.splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
for axis in self.axes:
self.splitter1.addWidget(axis.genAxis())
self.form_layout.addWidget(self.splitter1)
self.setMinimumWidth(400)
self.recipient = QtGui.QLineEdit(self)
# Add it to the form layout with a label
self.form_layout.addRow('&Recipient:', self.recipient)
# Add the form layout to the main VBox layout
self.layout.addLayout(self.form_layout, 0)
# Set the VBox layout as the window's main layout
self.setLayout(self.layout)
QtGui.QApplication.setStyle( QtGui.QStyleFactory.create('Cleanlooks') )
def run(self):
self.show()
def main():
qt_app = QtGui.QApplication(sys.argv)
window = Window()
window.run()
sys.exit(qt_app.exec_())
if __name__=="__main__":
main()

the reason it did not work was this line:
self.form_layout = QtGui.QFormLayout()
It should be:
self.form_layout = QtGui.QFormLayout(self.frame)

Related

I can't get window to resize when I hide a widget

I found some code on here that shows an example of how you can get the window to resize when the widget is hidden, and it works for me. Here is the code:
from PyQt4 import QtCore, QtGui
import sys
class MainWindow(QtGui.QWidget):
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
super(MainWindow, self).__init__()
self.button = QtGui.QPushButton('Show/Hide')
self.button.setCheckable(True)
self.frame = QtGui.QFrame()
self.frame.setFixedHeight(100)
self.layout = layout = QtGui.QVBoxLayout()
layout2 = QtGui.QVBoxLayout()
self.setLayout(layout)
self.frame.setLayout(layout2)
layout.addWidget(self.button)
layout.addWidget(self.frame)
layout.addStretch(1)
layout2.addWidget(QtGui.QLabel('Yoyoyo'))
self.button.toggled.connect(self.clickAction)
def startup(self):
self.show()
sys.exit(self.app.exec_())
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.resizeMe)
def resizeMe(self):
self.resize(self.minimumSizeHint())
if __name__ == "__main__":
myApp = MainWindow()
myApp.startup()
I then tried to modify this to match my existing code by separating the mainWindow class and the widget class. Here is the code that does that.
from PySide import QtGui,QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.w = testW(self)
self.setCentralWidget(self.w)
self.show()
class testW(QtGui.QWidget):
def __init__(self,parent):
super(testW,self).__init__()
self.parent = parent
self.button = QtGui.QPushButton('Show/Hide')
self.button.setCheckable(True)
self.button.setChecked(True);
self.frame = QtGui.QFrame()
self.frame.setFixedHeight(100)
self.layout = layout = QtGui.QVBoxLayout()
layout2 = QtGui.QVBoxLayout()
self.setLayout(layout)
self.frame.setLayout(layout2)
layout.addWidget(self.button)
layout.addWidget(self.frame)
layout.addStretch(1)
layout2.addWidget(QtGui.QLabel('Yoyoyo'))
self.button.toggled.connect(self.clickAction)
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.resizeMe)
def resizeMe(self):
self.resize(self.minimumSizeHint())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myApp = MainWindow()
sys.exit(app.exec_())
#time.sleep(1)
Running the first code does what I want it to. After I hide the widget, the window resizes to the correct size. The second implementation of the code does not shrink and expand the window when I hide and show the widget. Is this because the MainWindow is in a separate class?
Use size policies for your widgets. For your example you can change UI creation code as follows:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.w = testW(self)
self.w.setSizePolicy(
QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding
)
self.setCentralWidget(self.w)
self.show()
Please note new setSizePolicy call which say Qt layout engine how to change the size of your widget according to its content.
Unfortunately QMainWindow does not respect sizeHint automatically, but it is calculated properly, so you can adjustSize manually:
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.parent.adjustSize)
You do not need to resize your widget itself, because it will be resized according to the policy. Even sizeHint will be calculated automatically so you need only to call adjustSize of QMainWindow.
PS: I used PySide2 instead of PySide so the imports are different a little bit:
from PySide2 import QtWidgets, QtCore

PyQt5 adds only one widget to centralWidget

this is my very first post on stackoverflow. To this day stackoverflow has been a very huge help for me improving my python skills.
But I'm having this problem, that PyQt adds only one widget to the centralWidget instead of the 9x9 matrix I need for Sudoku. In another version - which worked - I used two classes to create the MainWindow and the Widgets seperatly. But now I want to achieve it in only one class.
link1: how it looks
link2: how it should look
import sys
from selenium import webdriver
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class mainwindow(QMainWindow, QWidget):
def __init__(self, parent = None):
super(mainwindow, self).__init__(parent = parent)
self.title = 'SUDOKU SOLVER'
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
# add menubar
menubar = self.menuBar()
# add drop down items
exitAct = QAction('&Exit', self)
exitAct.setShortcut('Ctrl+Q')
exitAct.setStatusTip('Exit Application')
exitAct.triggered.connect(qApp.quit)
newAct = QAction('New', self)
newAct.setShortcut('Ctrl+N')
newAct.setStatusTip('New Sudoku')
newAct.triggered.connect(GameLogic.clearFields)
rulesAct = QAction('Rules', self)
rulesAct.setShortcut('Ctrl+R')
rulesAct.setStatusTip('Sudoku Rules')
rulesAct.triggered.connect(GameLogic.sudokuRules)
# add menubar entries
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(newAct)
fileMenu.addAction(exitAct)
helpMenu = menubar.addMenu('&Help')
helpMenu.addAction(rulesAct)
# call gridlayout function
l = self.gridLayout()
self.setCentralWidget(l)
self.show()
def gridLayout(self):
layout = QGridLayout()
solve = QPushButton('Solve', self)
solve.clicked.connect(GameLogic.solveSudoku)
solve.setFixedSize(60, 30)
mainwindow.fields = {}
# validate user input
onlyInt = QIntValidator(1, 9, self)
# this is the part that doesnt work...
for x in range(9):
for y in range(9):
# keep a reference to the buttons
mainwindow.fields[(x, y)] = QLineEdit(self)
mainwindow.fields[(x, y)].setMaxLength(1)
mainwindow.fields[(x, y)].setValidator(onlyInt)
mainwindow.fields[(x, y)].setFixedSize(60, 60)
mainwindow.fields[(x, y)].setFont(QFont('Sans Serif', 20))
mainwindow.fields[(x, y)].setAlignment(Qt.AlignCenter)
# add to the layout
layout.addWidget(mainwindow.fields[(x, y)], x, y)
layout.addWidget(solve, 10, 4)
self.setLayout(layout)
class GameLogic():
def clearFields(self):
for i in range(9):
for j in range(9):
mainwindow.fields[(i, j)].clear()
def sudokuRules(self):
pass
def solveSudoku(self):
pass
def main():
app = QApplication(sys.argv)
ex = mainwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Without running it myself, or having used pyQt before,
setCentralWidget() needs a widget as a parameter.
You don't seem to return anything from gridLayout().
You might need to create a widget.
Use its layout to add all your things.
And then assign this widget via setCentralWidget().
P.S.: Maybe it works if you just remove the l = *; and setCentralWidget(); lines and just call gridLayout() (perhaps rename it to createGridLayout())
You have a few basic mistakes but nothing more.
Firstly, there's no need for mainwindow to inherit from both QMainWindow and QWidget, so
class mainwindow(QMainWindow, QWidget):
becomes...
class mainwindow(QMainWindow):
Secondly, in mainwindow::gridLayout you place your grid of controls in a layout and then do...
self.setLayout(layout)
That should almost certainly result in a warning along the lines of...
QWidget::setLayout: Attempting to set QLayout "" on mainwindow "",
which already has a layout
Instead, create a new QWidget and use it as the container for your new layout then return that QWidget from gridLayout. So...
self.setLayout(layout)
becomes...
w = QWidget()
w.setLayout(layout)
return(w)
Complete Code:
import sys
from selenium import webdriver
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class mainwindow(QMainWindow):
def __init__(self, parent = None):
super(mainwindow, self).__init__(parent = parent)
self.title = 'SUDOKU SOLVER'
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
# add menubar
menubar = self.menuBar()
# add drop down items
exitAct = QAction('&Exit', self)
exitAct.setShortcut('Ctrl+Q')
exitAct.setStatusTip('Exit Application')
exitAct.triggered.connect(qApp.quit)
newAct = QAction('New', self)
newAct.setShortcut('Ctrl+N')
newAct.setStatusTip('New Sudoku')
newAct.triggered.connect(GameLogic.clearFields)
rulesAct = QAction('Rules', self)
rulesAct.setShortcut('Ctrl+R')
rulesAct.setStatusTip('Sudoku Rules')
rulesAct.triggered.connect(GameLogic.sudokuRules)
# add menubar entries
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(newAct)
fileMenu.addAction(exitAct)
helpMenu = menubar.addMenu('&Help')
helpMenu.addAction(rulesAct)
# call gridlayout function
l = self.gridLayout()
self.setCentralWidget(l)
self.show()
def gridLayout(self):
layout = QGridLayout()
solve = QPushButton('Solve', self)
solve.clicked.connect(GameLogic.solveSudoku)
solve.setFixedSize(60, 30)
mainwindow.fields = {}
# validate user input
onlyInt = QIntValidator(1, 9, self)
# this is the part that doesnt work...
for x in range(9):
for y in range(9):
# keep a reference to the buttons
mainwindow.fields[(x, y)] = QLineEdit(self)
mainwindow.fields[(x, y)].setMaxLength(1)
mainwindow.fields[(x, y)].setValidator(onlyInt)
mainwindow.fields[(x, y)].setFixedSize(60, 60)
mainwindow.fields[(x, y)].setFont(QFont('Sans Serif', 20))
mainwindow.fields[(x, y)].setAlignment(Qt.AlignCenter)
# add to the layout
layout.addWidget(mainwindow.fields[(x, y)], x, y)
layout.addWidget(solve, 10, 4)
w = QWidget()
w.setLayout(layout)
return(w)
class GameLogic():
def clearFields(self):
for i in range(9):
for j in range(9):
mainwindow.fields[(i, j)].clear()
def sudokuRules(self):
pass
def solveSudoku(self):
pass
def main():
app = QApplication(sys.argv)
ex = mainwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Add widgets on the fly in pyside

Very new to pyside so maybe a stupid question. I want to create a pyside UI which has a variable number of items in it and also has the possibility to add items while it is running and to make it even more complex it also needs a scroll bar to fit it all on screen!
This is what I've got right now:
import sys
from PySide import QtGui
from PySide import QtCore
class example(QtGui.QWidget):
def __init__(self, parent= None):
super(example, self).__init__()
grid = QtGui.QGridLayout()
grid.setSpacing(10)
self.widget = QtGui.QWidget()
self.layout = QtGui.QGridLayout(self)
for i in range(5):
btn = QtGui.QPushButton("test"+str(i))
self.layout.addWidget(btn,i,0)
btn.clicked.connect(self.buttonClicked)
self.count = i
self.widget.setLayout(self.layout)
self.scroll = QtGui.QScrollArea()
self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.scroll.setWidget(self.widget)
grid.addWidget(self.scroll,3,0)
self.setLayout(grid)
def buttonClicked(self):
title = QtGui.QLabel('Title'+str(self.count))
self.layout.addWidget(title,self.count + 1,0)
self.count += 1
self.widget.addLayout(self.layout,0)
self.scroll.addWidget(self.widget,0)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialog = example()
dialog.show()
sys.exit(app.exec_())
But somehow the layout gets messed up when adding items through one of the buttons.
Does anybody have an idea how to fix this?
Thanx!
You're not far off. The key piece you're missing is QScrollArea.setWidgetResizable, which will ensure the scrollarea automatically resizes its viewport to fit the contents.
I've made some other adjustments to your example and added comments where appropriate:
class example(QtGui.QWidget):
def __init__(self, parent= None):
super(example, self).__init__()
grid = QtGui.QGridLayout()
grid.setSpacing(10)
self.widget = QtGui.QWidget()
# set the widget as parent of its own layout
self.layout = QtGui.QGridLayout(self.widget)
for i in range(5):
btn = QtGui.QPushButton("test"+str(i))
self.layout.addWidget(btn,i,0)
btn.clicked.connect(self.buttonClicked)
# following lines are redundant
# self.count = i
# self.widget.setLayout(self.layout)
self.scroll = QtGui.QScrollArea()
# need this so that scrollarea handles resizing
self.scroll.setWidgetResizable(True)
# these two lines may not be needed now
self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.scroll.setWidget(self.widget)
grid.addWidget(self.scroll, 3, 0)
self.setLayout(grid)
def buttonClicked(self):
title = QtGui.QLabel('Title' + str(self.layout.count()))
self.layout.addWidget(title)
# following lines are redundant
# self.layout.addWidget(title, self.count + 1, 0)
# self.count += 1
# self.widget.addLayout(self.layout,0)
# self.scroll.addWidget(self.widget,0)

widgets are not expandig according to window size

what is mistake in this code that prevents widgets from expanding according to window size ?
class FeedbackWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.main_layout = QVBoxLayout(self)
self.main_widget = QWidget(self)
self.main_widget.setLayout(self.main_layout)
self.title_label = QLabel("Tell us what you think:")
self.feedback_text_editor = QTextEdit()
self.send_button = QPushButton("Send")
self.main_layout.addWidget(self.title_label)
self.main_layout.addWidget(self.feedback_text_editor)
self.main_layout.addWidget(self.send_button)
self.setWindowTitle("Feedback")
self.setGeometry(200,120,300,300)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = FeedbackWindow()
w.show()
app.exec_()
the main layout and widget are connected to self, so it should take its dimension.
The code does not use self.main_widget. Remove self.main_widget:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class FeedbackWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.main_layout = QVBoxLayout(self)
#self.main_widget = QWidget(self) # main_widget is not used.
#self.main_widget.setLayout(self.main_layout)
self.setLayout(self.main_layout)
self.title_label = QLabel("Tell us what you think:")
self.feedback_text_editor = QTextEdit()
self.send_button = QPushButton("Send")
self.main_layout.addWidget(self.title_label)
self.main_layout.addWidget(self.feedback_text_editor)
self.main_layout.addWidget(self.send_button)
self.setWindowTitle("Feedback")
self.setGeometry(200,120,300,300)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = FeedbackWindow()
w.show()
app.exec_()
Remove
self.main_widget = QWidget(self)
self.main_widget.setLayout(self.main_layout)
You don't need them. In your implementation, the layout is set on self.main_widget which is NOT the main widget. Your main widget is your FeedbackWindows itself. When you call self.main_layout = QVBoxLayout(self), it implicitely apply the layout on the main widget.

How can I make a white box in PyQt?

I have a PyQt application that has a widget called Deck
class Deck(QtGui.QWidget):
def __init__(self, filename, parent):
super(Deck, self).__init__(parent)
self.setMinimumSize(100, 150)
self.setStyleSheet('background-color: white;')
label = QtGui.QLabel("deck", self)
label.show()
I expected the Deck widget to be all white, but it is only under the label, although it accepts clicks on its 100x150 area and adjusts its hbox:s size.
Edit:
The surrounding layout.
import sys
from PyQt4 import QtGui
app = QtGui.QApplication(sys.argv)
#import qt4reactor
#qt4reactor.install()
from deck import Deck
class Duel(QtGui.QWidget):
def __init__(self):
super(Duel, self).__init__()
topArea = QtGui.QHBoxLayout()
topArea.addStretch(1)
d = Deck(sys.argv[1], self)
d.show()
topArea.addWidget(d)
bottomArea = QtGui.QHBoxLayout()
d = Deck(sys.argv[2], self)
d.show()
bottomArea.addWidget(d)
bottomArea.addStretch(1)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(topArea)
vbox.addStretch(1)
vbox.addLayout(bottomArea)
self.setLayout(vbox)
def main():
root = Duel()
root.show()
app.exec_()
if __name__ == '__main__':
main()

Categories