PyQt5 adds only one widget to centralWidget - python

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

Related

Dynamically resize buttons in a Pyside GUI and manage Layouts

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

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)

How do I add a menubar to a QWidget?

I have a Qwidget thats usually is displayed in a Qmainwindow.
Sometimes its unnecessary to use the whole mainwindow, because you only want to use functions from a certain Qwidget. If that's the case, I want a menubar in my widget.
I tried:
if parent == "self":
self.layout().addMenubar(self)
But using the code above the its just stops compiling without raising any error.
What Im doing wrong? Thanks!
OK, I can do !
You just add QtGui.QMenuBar(self) in your QWidget and implement just like QMainWindows.
Reference : Here
Example;
import sys
from PyQt4 import QtGui
class QTestWidget (QtGui.QWidget):
def __init__ (self):
super(QTestWidget, self).__init__()
self.myQMenuBar = QtGui.QMenuBar(self)
exitMenu = self.myQMenuBar.addMenu('File')
exitAction = QtGui.QAction('Exit', self)
exitAction.triggered.connect(QtGui.qApp.quit)
exitMenu.addAction(exitAction)
myQApplication = QtGui.QApplication(sys.argv)
myQTestWidget = QTestWidget()
myQTestWidget.show()
myQApplication.exec_()
Regards,
There is also a pretty clean way to combine QMainWindow with QWidget, using two classes:
import sys
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.win_widget = WinWidget(self)
widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout(widget)
layout.addWidget(self.win_widget)
self.setCentralWidget(widget)
self.statusBar().showMessage('Ready')
self.toolbar = self.addToolBar('Exit')
exitAction = QtGui.QAction ('Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(QtGui.qApp.quit)
self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(exitAction)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
self.setGeometry(300, 300, 450, 250)
self.setWindowTitle('Test')
self.setWindowIcon (QtGui.QIcon('logo.png'))
self.show()
class WinWidget (QtGui.QWidget) :
def __init__(self, parent):
super (WinWidget , self).__init__(parent)
self.controls()
#self.__layout()
def controls(self):
self.qbtn = QtGui.QPushButton('Quit', self)
self.qbtn.setFixedSize (100,25)
self.qbtn.setToolTip ("quit")
self.qbtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
self.qbtn.move(50, 50)
def main():
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
This works :
def menu_bar (self) :
self.menuBar = QtGui.QMenuBar (self)
fileMenu = self.menuBar.addMenu ("File")
self.menuBar.show()
or already with actions :
def menu_bar (self) :
self.menuBar = QtGui.QMenuBar (self)
fileMenu = self.menuBar.addMenu ("File")
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
fileMenu.addAction(exitAction)
exitAction.triggered.connect(self.close)
exitAction.setShortcut('Ctrl+Q')
self.menuBar.show()
It will be a good idea to keep using QMainWindow since QMenuBar is designed to be used within it.
That said, I found this post helpful when I was also looking into doing same:
Qt QWidget add menubar
See if it's the solution that can help you. It helped me though

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.

Categories