Dynamically resize buttons in a Pyside GUI and manage Layouts - python

I have read the documentation on the following matter, but QtGui is so overwhelmingly complex I might have missed the piece.
I have created a GUI, in which it consists of a menubar two QLabel and two QLineEdit and a button. The issue I am facing in my code is the button is getting placed on an absolute co-ordinate position and does not dynamically resize according to the window resizing and the QLineEdit box is displayed with a certain horizontal shift from the QLabel. But I would like to place it next to the QLabel. I have attached a pic of the GUI which I am getting. Here is my code
import sys
from PySide.QtGui import *
from PySide.QtCore import *
class guiwindow(QMainWindow):
def __init__(self):
super(guiwindow,self).__init__()
self.central = QWidget()
self.setCentralWidget(self.central)
self.setGeometry(400, 100, 1200, 800)
self.setWindowTitle(" Automatic Selector")
self.menubar()
self.makebuttons()
self.angles()
def menubar(self):
textEdit = QWidget()
self.setCentralWidget(textEdit)
exitAction = QAction('Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.close)
self.statusBar()
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(exitAction)
def makebuttons(self):
# self.central_widget = QWidget()
# self.setCentralWidget(self.central_widget)
button = QPushButton("Test", self)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(button)
# self.central_widget.setLayout(hbox)
self.show()
def angles(self):
self.window = QWidget()
self.setCentralWidget(self.window)
self.Rotation = QLabel('Rotation:')
self.Tilt = QLabel('Tilt:')
self.RotationEdit = QLineEdit()
self.RotationEdit.setFixedWidth(55)
self.TiltEdit = QLineEdit()
self.TiltEdit.setFixedWidth(55)
self.grid = QGridLayout()
self.grid.addWidget(self.Rotation,1,0,Qt.AlignLeft)
self.grid.addWidget(self.RotationEdit,1,1,Qt.AlignLeft)
self.grid.addWidget(self.Tilt,2,0,Qt.AlignLeft)
self.grid.addWidget(self.TiltEdit, 2,1,Qt.AlignLeft)
self.window.setLayout(self.grid)
def main():
app = QApplication(sys.argv)
ex = guiwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
And if I take out
self.window = QWidget()
self.setCentralWidget(self.window)
from the def angles(self): the Rotation angle and the tilt angle does not appear on the GUI. Why does this

Related

PyQt5 add a scrollbar to main window

I know there are already lots of threads opened with this topic, I was trying to follow their recommendations, but I still struggle to achieve this.
Here is my initial code for window:
from PyQt5.QtWidgets import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Python ")
self.setGeometry(100, 100, 600, 400)
self.UiComponents()
self.show()
def UiComponents(self):
emphysemaLabel = QLabel("EMPHYSEMA", self)
emphysemaLabel.move(10, 10)
ggoLabel = QLabel("GGO", self)
ggoLabel.move(10, 300)
condensLabel = QLabel("Condens", self)
condensLabel.move(10, 590)
emphysema_graph_lin = QLabel(self)
emphysema_graph_lin.resize(302, 232)
emphysema_graph_lin.move(10, 50)
emphysema_graph_lin.setStyleSheet("background-color:yellow;")
ggo_graph_lin = QLabel(self)
ggo_graph_lin.resize(302, 232)
ggo_graph_lin.move(10, 340)
ggo_graph_lin.setStyleSheet("background-color:yellow;")
condens_graph_lin = QLabel(self)
condens_graph_lin.resize(302, 232)
condens_graph_lin.move(10, 630)
condens_graph_lin.setStyleSheet("background-color:yellow;")
if __name__ == "__main__":
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())
An example if what I found useful and working is code found here https://www.pythonguis.com/tutorials/qscrollarea/
I tried to apply it to my code, like this:
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Python ")
self.setGeometry(100, 100, 600, 400)
self.UiComponents()
self.show()
def UiComponents(self):
self.scroll = QScrollArea() # Scroll Area which contains the widgets, set as the centralWidget
self.widget = QWidget() # Widget that contains the collection of Vertical Box
self.vbox = QVBoxLayout() # The Vertical Box that contains the Horizontal Boxes of labels and buttons
emphysemaLabel = QLabel("EMPHYSEMA", self)
emphysemaLabel.move(10, 10)
self.vbox.addWidget(emphysemaLabel)
ggoLabel = QLabel("GGO", self)
ggoLabel.move(10, 300)
self.vbox.addWidget(ggoLabel)
condensLabel = QLabel("Condens", self)
condensLabel.move(10, 590)
self.vbox.addWidget(condensLabel)
emphysema_graph_lin = QLabel(self)
emphysema_graph_lin.resize(302, 232)
emphysema_graph_lin.move(10, 50)
emphysema_graph_lin.setStyleSheet("background-color:yellow;")
self.vbox.addWidget(emphysema_graph_lin)
ggo_graph_lin = QLabel(self)
ggo_graph_lin.resize(302, 232)
ggo_graph_lin.move(10, 340)
ggo_graph_lin.setStyleSheet("background-color:yellow;")
self.vbox.addWidget(ggo_graph_lin)
condens_graph_lin = QLabel(self)
condens_graph_lin.resize(302, 232)
condens_graph_lin.move(10, 630)
condens_graph_lin.setStyleSheet("background-color:yellow;")
self.vbox.addWidget(condens_graph_lin)
self.widget.setLayout(self.vbox)
# Scroll Area Properties
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.widget)
self.setCentralWidget(self.scroll)
if __name__ == "__main__":
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())
But it's not working, how should I do it?
Thank you for any advice.
You can add a vertical spacer at the end of the layout using the addStretch() method of the QVBoxLayout object.
And adjust the maximum size to view the scroll working, setMaximumSize().
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Python ")
self.setGeometry(100, 100, 600, 400)
# set maximum size of the QMainWindow
self.setMaximumSize(600, 100)
self.UiComponents()
self.show()
def UiComponents(self):
self.scroll = QScrollArea() # Scroll Area which contains the widgets, set as the centralWidget
self.widget = QWidget() # Widget that contains the collection of Vertical Box
self.vbox = QVBoxLayout() # The Vertical Box that contains the Horizontal Boxes of labels and buttons
emphysemaLabel = QLabel("EMPHYSEMA", self)
emphysemaLabel.move(10, 10)
self.vbox.addWidget(emphysemaLabel)
ggoLabel = QLabel("GGO", self)
ggoLabel.move(10, 300)
self.vbox.addWidget(ggoLabel)
condensLabel = QLabel("Condens", self)
condensLabel.move(10, 590)
self.vbox.addWidget(condensLabel)
emphysema_graph_lin = QLabel(self)
emphysema_graph_lin.resize(302, 232)
emphysema_graph_lin.move(10, 50)
emphysema_graph_lin.setStyleSheet("background-color:yellow;")
self.vbox.addWidget(emphysema_graph_lin)
ggo_graph_lin = QLabel(self)
ggo_graph_lin.resize(302, 232)
ggo_graph_lin.move(10, 340)
ggo_graph_lin.setStyleSheet("background-color:yellow;")
self.vbox.addWidget(ggo_graph_lin)
condens_graph_lin = QLabel(self)
condens_graph_lin.resize(302, 232)
condens_graph_lin.move(10, 630)
condens_graph_lin.setStyleSheet("background-color:yellow;")
self.vbox.addWidget(condens_graph_lin)
# add a vertical spacer with addStretch() method
self.vbox.addStretch()
self.widget.setLayout(self.vbox)
# Scroll Area Properties
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.widget)
self.setCentralWidget(self.scroll)
if __name__ == "__main__":
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

How to display Qframe as actual size in PyQt5?

Here is My program. My Requirement: Display frame as mentioned size. But in my code its occupies the entire area. How to resolve it?
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class FrameExample(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Frame Example")
self.setGeometry(100,100,1500,900)
self.frame = QFrame()
self.frame.resize(300, 300)
self.frame.setStyleSheet("background-color:skyblue")
self.frame1 = QFrame()
self.frame1.setGeometry(QRect(10,10,600,600))
self.frame1.resize(600,600)
self.frame1.setStyleSheet("background-color:lightgreen")
layout = QVBoxLayout()
layout.addWidget(self.frame)
layout.addWidget(self.frame1)
self.setLayout(layout)
if __name__=="__main__":
app = QApplication(sys.argv)
countrywin =FrameExample()
countrywin.show()
sys.exit(app.exec_())
With resize() you are managing the size of the widget but after setting the layouts it will handle the size. One possible solution is to use setFixedSize():
class FrameExample(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Frame Example")
self.setGeometry(100, 100, 1500, 900)
self.frame = QFrame()
self.frame.setFixedSize(300, 300)
self.frame.setStyleSheet("background-color:skyblue")
self.frame1 = QFrame()
self.frame1.setFixedSize(600, 600)
self.frame1.setStyleSheet("background-color:lightgreen")
layout = QVBoxLayout(self)
layout.addWidget(self.frame)
layout.addWidget(self.frame1)

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()

Building Qt Gui from few classes together

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)

PyQt QScrollArea within QScrollArea

I am trying to use multiple horizontal sub QScrollAreas with text and one vertical container QScrollArea. The idea being that the text area in the horizontal sub QScrollAreas will always have equivalent vertical heights and I would like to have one vertical QScrollArea to control the data within them.
The code below shows that the horizontal sub QScrollAreas work, but the vertical QScrollArea doesn't detect that the line edits within the widget inside it don't fit vertically. If I change
scroll.setWidgetResizable(True)
for the vertical QScrollArea to False, the vertical QScrollArea detects the widget inside doesn't fit but I want to be able to scroll all the lineEdits up and down not the parent widget. Also I would like all scrollbars to be always visible. Is this possible?
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Widget(QWidget):
def __init__(self, parent= None):
super(Widget, self).__init__()
self.setGeometry(100, 100, 400, 400)
baseWidget = QWidget()
hBox = QHBoxLayout()
hBox.addWidget(self.getWidget())
hBox.addWidget(self.getWidget())
baseWidget.setLayout(hBox)
scroll = QScrollArea()
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
#when set to False all scrolls are not visible and can only scroll parent widget not the data areas
scroll.setWidgetResizable(True)
scroll.setWidget(baseWidget)
vBox = QHBoxLayout()
vBox.addWidget(scroll)
self.setLayout(vBox)
def getWidget(self):
widget = QWidget()
layout = QVBoxLayout()
for i in range(20):
lineEdit = QLineEdit("row: "+str(i)+" data: "+str(list(range(10))))
lineEdit.setMinimumWidth(250)
layout.addWidget(lineEdit)
widget.setLayout(layout)
scroll = QScrollArea()
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setWidgetResizable(False)
scroll.setWidget(widget)
return scroll
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = Widget()
dialog.show()
The answer could be found here:
PyQt4 : is there any signal related to scrollbar?
Just needed to sync vertical scrollbars and hide all but one:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Widget(QWidget):
def __init__(self, parent= None):
super(Widget, self).__init__()
self.setGeometry(100, 100, 200, 200)
baseWidget = QWidget()
hBox = QHBoxLayout()
lscrollArea = self.getWidget(False)
rScrollArea = self.getWidget(True)
rScrollArea.verticalScrollBar().valueChanged.connect(
lscrollArea.verticalScrollBar().setValue)
hBox.addWidget(lscrollArea)
hBox.addWidget(rScrollArea)
baseWidget.setLayout(hBox)
vBox = QHBoxLayout()
vBox.addWidget(baseWidget)
self.setLayout(vBox)
def getWidget(self, vScrollOn):
widget = QWidget()
layout = QVBoxLayout()
for i in range(20):
lineEdit = QLineEdit("row: "+str(i)+" data: "+str(list(range(10))))
lineEdit.setMinimumWidth(250)
layout.addWidget(lineEdit)
widget.setLayout(layout)
scroll = QScrollArea()
if vScrollOn:
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
else:
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setWidgetResizable(False)
scroll.setWidget(widget)
return scroll
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = Widget()
dialog.show()
app.exec_()

Categories