When I decrease the window size all the widgets disappear.I want the widgets to move along when size is decreased.How do I solve this problem?
I have a drop-down menu from which a value is selected.When an "Add cmd" button is pressed the value is added to edit box.
Thanks in advance.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class tabdemo(QTabWidget):
def __init__(self, parent = None):
super(tabdemo, self).__init__(parent)
self.setGeometry(50, 50, 400,400)
QShortcut(QKeySequence("Esc"), self, self.close)
self.tab1 = QWidget()
self.tab2 = QWidget()
self.addTab(self.tab1,"Tab 1")
self.tab1UI()
self.setWindowTitle("Main Window")
def tab1UI(self):
self.comboBox = QComboBox(self.tab1)
self.comboBox.addItem('ABC')
self.comboBox.addItem('BCD')
self.comboBox.addItem('CDE')
self.comboBox.move(5,20)
self.comboBox.resize(180,30)
self.button = QPushButton('Add Cmd', self.tab1)
self.button.move(190,20)
self.button.resize(80,30)
self.button.clicked.connect(self.handleTest)
self.b = QTextEdit(self.tab1)
self.b.move(20,75)
self.b.resize(290,200)
self.button = QPushButton('Send Batch', self.tab1)
self.button.move(40,300)
self.button.resize(150,30)
self.button = QPushButton('Clear', self.tab1)
self.button.move(200,300)
self.button.resize(80,30)
self.button.clicked.connect(self.deletevalue)
layout = QFormLayout()
self.setTabText(4,"BatchCMDS")
self.tab1.setLayout(layout)
def handleTest(self):
self.b.append(str(self.comboBox.currentText()))
def deletevalue(self):
self.b.clear()
def main():
app = QApplication(sys.argv)
ex = tabdemo()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
If you want the widgets to adapt to the size of the window you should use layouts, For this, the application must be designed, for this an image is used of how you want your application to be:
As we see the widgets that are inside a tab are divided into 3 groups, the first is made up of the QComboBox with the QPushButton, the second the QTextEdit, and the third the 2 remaining buttons. Each group is horizontally distributed, so in that case we should use QHBoxLayout except the QTextEdit that is alone, and each group should be in QVBoxLayout. I do not understand why you use the QFormLayout, also if you use the layouts the positions are not necessary.
Another error that I see in your code is that several buttons have the same name, this causes errors like for example that the Add CMD button does not work, you must give a different name to each widget.
class tabdemo(QTabWidget):
def __init__(self, parent = None):
super(tabdemo, self).__init__(parent)
self.setGeometry(50, 50, 400,400)
QShortcut(QKeySequence("Esc"), self, self.close)
self.tab1 = QWidget()
self.tab2 = QWidget()
self.addTab(self.tab1,"Tab 1")
self.addTab(self.tab2,"Tab 2")
self.tab1UI()
def tab1UI(self):
vlayout = QVBoxLayout(self.tab1)
hlayout1 = QHBoxLayout()
self.comboBox = QComboBox(self.tab1)
self.comboBox.addItems(['ABC', 'BCD', 'CDE'])
self.button = QPushButton('Add Cmd', self.tab1)
self.button.clicked.connect(self.handleTest)
hlayout1.addWidget(self.comboBox)
hlayout1.addWidget(self.button)
hlayout1.addItem(QSpacerItem(100, 10, QSizePolicy.Expanding, QSizePolicy.Preferred))
vlayout.addLayout(hlayout1)
self.b = QTextEdit(self.tab1)
vlayout.addWidget(self.b)
hlayout2 = QHBoxLayout()
self.buttonSend = QPushButton('Send Batch', self.tab1)
self.buttonClear = QPushButton('Clear', self.tab1)
self.buttonClear.clicked.connect(self.deletevalue)
hlayout2.addItem(QSpacerItem(100, 10, QSizePolicy.Expanding, QSizePolicy.Preferred))
hlayout2.addWidget(self.buttonSend)
hlayout2.addWidget(self.buttonClear)
hlayout2.addItem(QSpacerItem(100, 10, QSizePolicy.Expanding, QSizePolicy.Preferred))
vlayout.addLayout(hlayout2)
self.setTabText(4,"BatchCMDS")
def handleTest(self):
self.b.append(self.comboBox.currentText())
def deletevalue(self):
self.b.clear()
Related
Using PyQt5, I am creating multiple windows. These windows should be able to change the view without popping new windows. However, when adding another window to the mainwindow with exactly the same layout and buttons, the window layouts differ.
What I want is to use exactly the same layout options as the Main window. My code can be found below:
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import Qt
import sys
class mainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# MAIN WINDOW SETTINGS
self.central_widget = QtWidgets.QStackedWidget()
self.setGeometry(400, 150, 1200, 700)
self.setWindowTitle('Main window')
# COMPOSE MAIN WINDOW
self.settings = {'grid': {'row': [5,0,0,0,1], 'column': [1,3,1,1]}}
self.grid = QtWidgets.QGridLayout()
self.grid.setVerticalSpacing(20)
for row, stretch in enumerate(self.settings['grid']['row']):
self.grid.setRowStretch(row, stretch)
for column, stretch in enumerate(self.settings['grid']['column']):
self.grid.setColumnStretch(column, stretch)
self.b1 = QtWidgets.QPushButton("Button 1")
self.b1.clicked.connect(lambda: self.b1_handler('second window'))
self.b2 = QtWidgets.QPushButton("Button 2")
self.b2.clicked.connect(self.b2_handler)
self.b3 = QtWidgets.QPushButton("Button 3")
self.b3.clicked.connect(self.b3_handler)
self.b4 = QtWidgets.QPushButton("Button 4")
self.b4.clicked.connect(self.b4_handler)
self.b5 = QtWidgets.QPushButton("Button 5")
self.b5.clicked.connect(self.b5_handler)
self.grid.addWidget(self.b1, 1, 1)
self.grid.addWidget(self.b2, 1, 2)
self.grid.addWidget(self.b3, 2, 1)
self.grid.addWidget(self.b4, 3, 1)
self.grid.addWidget(self.b5, 4, 3)
self.main_window = QtWidgets.QWidget()
self.main_window.setLayout(self.grid)
# TEST WINDOW
self.second_window = Second_window(self)
# FINISH MAIN WINDOW AND SHOW INITIAL SCREEN
self.central_widget.addWidget(self.main_window)
self.central_widget.addWidget(self.second_window)
self.setCentralWidget(self.central_widget)
self.current_window = self.main_window
def b1_handler(self, to_window):
if to_window == 'second window':
self.current_window.hide()
self.second_window.show()
self.current_window = self.second_window
elif to_window == 'main window':
self.current_window.hide()
self.main_window.show()
self.current_window = self.main_window
def b2_handler(self):
pass
def b3_handler(self):
pass
def b4_handler(self):
pass
def b5_handler(self):
pass
class Second_window(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__()
self.parent = parent
# Create grid
self.settings = {'grid': {'row': [5,0,0,0,1], 'column': [1,3,1,1]}}
self.grid = QtWidgets.QGridLayout()
self.grid.setVerticalSpacing(20)
for row, stretch in enumerate(self.settings['grid']['row']):
self.grid.setRowStretch(row, stretch)
for column, stretch in enumerate(self.settings['grid']['column']):
self.grid.setColumnStretch(column, stretch)
# CREATE WIDGETS ON PAGE
self.b1 = QtWidgets.QPushButton("Button 1")
self.b1.clicked.connect(self.b1_handler)
self.b2 = QtWidgets.QPushButton("Button 2")
self.b2.clicked.connect(self.b2_handler)
self.b3 = QtWidgets.QPushButton("Button 3")
self.b3.clicked.connect(self.b3_handler)
self.b4 = QtWidgets.QPushButton("Button 4")
self.b4.clicked.connect(self.b4_handler)
self.b5 = QtWidgets.QPushButton("Button 5")
self.b5.clicked.connect(self.b5_handler)
self.grid.addWidget(self.b1, 1, 1)
self.grid.addWidget(self.b2, 1, 2)
self.grid.addWidget(self.b3, 2, 1)
self.grid.addWidget(self.b4, 3, 1)
self.grid.addWidget(self.b5, 4 ,3)
# SET LAYOUT
self.setLayout(self.grid)
def b1_handler(self):
pass
def b2_handler(self):
pass
def b3_handler(self):
pass
def b4_handler(self):
pass
def b5_handler(self):
self.parent.b1_handler('main window')
def main():
app = QtWidgets.QApplication(sys.argv)
App = mainWindow()
App.show()
# sys.exit(app.exec_())
currentExitCode = app.exec_()
if __name__ == '__main__':
main()
With this code, my windows looks like this:
Main window:
Second window:
From the main window, I use button 1 to navigate to the second window. From the second window, I use button 5 to navigate back to the main window. What do I not understand properly?
Forcing the visibility of widgets added to a QStackedWidget isn't the proper way to display them, as the stacked layout won't be notified about the change and will not be able to properly relay the resize events.
To switch between widgets you must use the functions provided by the class, as clearly explained in the main documentation of QStackedWidget:
The index of the widget that is shown on screen is given by currentIndex() and can be changed using setCurrentIndex(). In a similar manner, the currently shown widget can be retrieved using the currentWidget() function, and altered using the setCurrentWidget() function.
Change to:
def b1_handler(self, to_window):
if to_window == 'second window':
self.central_widget.setCurrentWidget(self.second_window)
elif to_window == 'main window':
self.central_widget.setCurrentWidget(self.main_window)
You also don't need a reference to the current page, as currentWidget() already provides that dynamically.
I am trying the whole morning already to fix that.
So I have a PyQt Main Window where I want to display two widgets.
In the first widget there are articles listed (which works so far).
When I click on them until now a QMessageBox is opening, but I want that
a second widget is opening where I can read the RSS Feed.
But this is not working. See Code below:
class ArticleWidgets(QWidget):
def __init__(self, *args):
super().__init__(*args)
self.setGeometry(610, 610, 600, 600)
self.initUi()
def initUi(self):
self.box = QHBoxLayout(self)
def show(self, feed=None):
self.title = QLabel()
self.summary = QLabel()
self.link = QLabel()
if feed:
self.title.setText(feed[0])
self.summary.setText(feed[1])
self.link.setText(feed[2])
self.box.addWidget(self.title)
self.box.addWidget(self.summary)
self.box.addWidget(self.link)
self.setLayout(self.box)
class TitleWidgets(QWidget):
def __init__(self, *args):
super().__init__(*args)
self.setGeometry(10, 10, 600, 600)
self.initUi()
def initUi(self):
vbox = QHBoxLayout(self)
self.titleList = QListWidget()
self.titleList.itemDoubleClicked.connect(self.onClicked)
self.titleList.setGeometry(0, 0, 400, 400)
self.news = ANFFeed()
for item in self.news.all_feeds:
self.titleList.addItem(item[0])
vbox.addWidget(self.titleList)
def onClicked(self, item):
feeds = self.news.all_feeds
id = 0
for elem in range(len(feeds)):
if feeds[elem][0] == item.text():
id = elem
summary = feeds[id][1] + '\n\n'
link = feeds[id][2]
if feeds and id:
#ANFApp(self).show_articles(feeds[id])
show = ANFApp()
show.show_articles(feed=feeds[id])
QMessageBox.information(self, 'Details', summary + link)
class ANFApp(QMainWindow):
def __init__(self, *args):
super().__init__(*args)
self.setWindowState(Qt.WindowMaximized)
self.setWindowIcon(QIcon('anf.png'))
self.setAutoFillBackground(True)
self.anfInit()
self.show()
def anfInit(self):
self.setWindowTitle('ANF RSS Reader')
TitleWidgets(self)
#article_box = ArticleWidgets(self)
exitBtn = QPushButton(self)
exitBtn.setGeometry(600, 600, 100, 50)
exitBtn.setText('Exit')
exitBtn.setStyleSheet("background-color: red")
exitBtn.clicked.connect(self.exit)
def show_articles(self, feed=None):
present = ArticleWidgets()
present.show(feed)
def exit(self):
QCoreApplication.instance().quit()
Solution using Pyqtgraph's Docks and QTextBrowser
Here is a code trying to reproduce your sketch. I used the Pyqtgraph module (Documentation here: Pyqtgraph's Documentation and Pyqtgraph's Web Page) because its Dock widget is easier to use and implement from my perspective.
You must install the pyqtgraph module before trying this code:
import sys
from PyQt5 import QtGui, QtCore
from pyqtgraph.dockarea import *
class DockArea(DockArea):
## This is to prevent the Dock from being resized to te point of disappear
def makeContainer(self, typ):
new = super(DockArea, self).makeContainer(typ)
new.setChildrenCollapsible(False)
return new
class MyApp(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
central_widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
label = QtGui.QLabel('This is a label, The widgets will be below')
label.setMaximumHeight(15)
## The DockArea as its name says, is the are where we place the Docks
dock_area = DockArea(self)
## Create the Docks and change some esthetic of them
self.dock1 = Dock('Widget 1', size=(300, 500))
self.dock2 = Dock('Widget 2', size=(400, 500))
self.dock1.hideTitleBar()
self.dock2.hideTitleBar()
self.dock1.nStyle = """
Dock > QWidget {
border: 0px solid #000;
border-radius: 0px;
}"""
self.dock2.nStyle = """
Dock > QWidget {
border: 0px solid #000;
border-radius: 0px;
}"""
self.button = QtGui.QPushButton('Exit')
self.widget_one = WidgetOne()
self.widget_two = WidgetTwo()
## Place the Docks inside the DockArea
dock_area.addDock(self.dock1)
dock_area.addDock(self.dock2, 'right', self.dock1)
## The statment above means that dock2 will be placed at the right of dock 1
layout.addWidget(label)
layout.addWidget(dock_area)
layout.addWidget(self.button)
## Add the Widgets inside each dock
self.dock1.addWidget(self.widget_one)
self.dock2.addWidget(self.widget_two)
## This is for set the initial size and posotion of the main window
self.setGeometry(100, 100, 600, 400)
## Connect the actions to functions, there is a default function called close()
self.widget_one.TitleClicked.connect(self.dob_click)
self.button.clicked.connect(self.close)
def dob_click(self, feed):
self.widget_two.text_box.clear()
## May look messy but wat i am doing is somethin like this:
## 'Title : ' + feed[0] + '\n\n' + 'Summary : ' + feed[1]
self.widget_two.text_box.setText(
'Title : ' + feed[0]\
+ '\n\n' +\
'Summary : ' + feed[1]
)
class WidgetOne(QtGui.QWidget):
## This signal is created to pass a "list" when it (the signal) is emited
TitleClicked = QtCore.pyqtSignal([list])
def __init__(self):
QtGui.QWidget.__init__(self)
self.layout = QtGui.QVBoxLayout()
self.setLayout(self.layout)
self.titleList = QtGui.QListWidget()
self.label = QtGui.QLabel('Here is my list:')
self.layout.addWidget(self.label)
self.layout.addWidget(self.titleList)
self.titleList.addItem(QtGui.QListWidgetItem('Title 1'))
self.titleList.addItem(QtGui.QListWidgetItem('Title 2'))
self.titleList.itemDoubleClicked.connect(self.onClicked)
def onClicked(self, item):
## Just test values
title = item.text()
summary = "Here you will put the summary of {}. ".format(title)*50
## Pass the values as a list in the signal. You can pass as much values
## as you want, remember that all of them have to be inside one list
self.TitleClicked.emit([title, summary])
class WidgetTwo(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.layout = QtGui.QVBoxLayout()
self.setLayout(self.layout)
self.label2 = QtGui.QLabel('Here we show results?:')
self.text_box = QtGui.QTextBrowser()
self.layout.addWidget(self.label2)
self.layout.addWidget(self.text_box)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Again, there are comments inside the code to help you understand what I did.
Here is how it looks:
If you pass the mouse between the two widgets you will see the mouse icon will change, with that you can readjust on the run the size of both widgets.
Final Words
This is another approach, more "interactive" and more esthetic than my previous answer. As you said, using a QSplitter works too.
Problems
The way you are building your GUI is, in my opinion, messy and it may lead to errors. I suggest the use of Layouts for a more organized GUI.
The other problem is that each widget is an independent class so if you want to connect an action in one widget to do something in the other widget through the Main Window, you must use Signals.
Edit : Another suggestion, use other name for the close function instead of exit and try using self.close() instead of QCoreApplication.instance().quit()
Solution
Trying to emulate what you want to do I made this GUI:
import sys
from PyQt5 import QtGui, QtCore
class MyWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
## Generate the structure parts of the MainWindow
self.central_widget = QtGui.QWidget() # A QWidget to work as Central Widget
self.layout1 = QtGui.QVBoxLayout() # Vertical Layout
self.layout2 = QtGui.QHBoxLayout() # Horizontal Layout
self.widget_one = WidgetOne()
self.widget_two = WidgetTwo()
self.exitBtn = QtGui.QPushButton('Exit')
## Build the structure
# Insert a QWidget as a central widget for the MainWindow
self.setCentralWidget(self.central_widget)
# Add a principal layout for the widgets/layouts you want to add
self.central_widget.setLayout(self.layout1)
# Add widgets/layuts, as many as you want, remember they are in a Vertical
# layout: they will be added one below of the other
self.layout1.addLayout(self.layout2)
self.layout1.addWidget(self.exitBtn)
# Here we add the widgets to the horizontal layout: one next to the other
self.layout2.addWidget(self.widget_one)
self.layout2.addWidget(self.widget_two)
## Connect the signal
self.widget_one.TitleClicked.connect(self.dob_click)
def dob_click(self, feed):
## Change the properties of the elements in the second widget
self.widget_two.title.setText('Title : '+feed[0])
self.widget_two.summary.setText('Summary : '+feed[1])
## Build your widgets same as the Main Window, with the excepton that here you don't
## need a central widget, because it is already a widget.
class WidgetOne(QtGui.QWidget):
TitleClicked = QtCore.pyqtSignal([list]) # Signal Created
def __init__(self):
QtGui.QWidget.__init__(self)
##
self.layout = QtGui.QVBoxLayout() # Vertical Layout
self.setLayout(self.layout)
self.titleList = QtGui.QListWidget()
self.label = QtGui.QLabel('Here is my list:')
self.layout.addWidget(self.label)
self.layout.addWidget(self.titleList)
self.titleList.addItem(QtGui.QListWidgetItem('Title 1'))
self.titleList.addItem(QtGui.QListWidgetItem('Title 2'))
self.titleList.itemDoubleClicked.connect(self.onClicked)
def onClicked(self, item):
## Just test parameters and signal emited
self.TitleClicked.emit([item.text(), item.text()+item.text()])
class WidgetTwo(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.layout = QtGui.QVBoxLayout()
self.setLayout(self.layout)
self.title = QtGui.QLabel('Title : ---')
self.summary = QtGui.QLabel('Summary : ---')
self.link = QtGui.QLabel('Link : ---')
self.layout.addWidget(self.title)
self.layout.addWidget(self.summary)
self.layout.addWidget(self.link)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
Inside the code, there are comments to help you understand why I did to build an organized GUI. There is also an example of a Signal being used to connect the action of itemDoubleClicked from the first widget to the second one. Here is how the MainWindow looks:
It is not very clear how the layouts work just from seeing the result, so I did a little paint over to a better understanding:
The blue box is the vertical layout (QVBoxLayout) and the red one is the horizontal layout (QHBoxLayout). Inside the blue layout, are located the red layout (above) and the exit button (below); and inside the red layout, are located the widget_1 (left) and the widget_2 (right).
Other Solution
An "easier" solution will be building the widgets inside the MainWindow instead of creating separate classes. With this you will avoid the use of signals, but the code will become a little more confusing because all the code will be cramped in one class.
I want to add widgets in GUI when a user selects a particular item from QComboBox.
With the different options in combo-box Pip config, I want GUI to look like as in the following images. In the right image, there are extra widgets present for an item Multi pip. Also I want the location of the extra widgets as shown in the right image.
How to add these widgets dynamically ? Please find the code below.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt, QRect
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
vbox = QVBoxLayout()
CpsLabel = QLabel()
CpsLabel.setText("<font size = 12>Cps</font>")
CpsLabel.setAlignment(Qt.AlignCenter)
CpsLabel.setTextFormat(Qt.RichText)
CpsPipConfigLabel = QLabel('Pip config: ')
CpsPipConfigComboBox = QComboBox()
CpsPipConfigComboBox.addItems(['Single pip', 'Dual pip', 'Multi pip'])
CpsPipConfigComboBox.setCurrentIndex(2)
CpsChannel = QLabel('Cps channel: ')
CpsChannelComboBox = QComboBox()
CpsChannelComboBox.addItems(['A', 'B', 'C', 'D'])
CpsChannelComboBox.setCurrentIndex(0)
CpsTotalTeethLabel = QLabel('Total teeth: ')
CpsTotalTeethEdit = QLineEdit()
CpsTotalTeethEdit.setFixedWidth(50)
CpsTotalTeethEdit.setPlaceholderText('18')
CpsTotalTeethEdit.setValidator(QIntValidator())
CpsMissingTeethLabel = QLabel('Missing teeth: ')
CpsMissingTeethEdit = QLineEdit()
CpsMissingTeethEdit.setFixedWidth(50)
CpsMissingTeethEdit.setPlaceholderText('1')
CpsMissingTeethEdit.setValidator(QIntValidator())
vbox.addWidget(CpsLabel)
vbox.addStretch()
CpsQHBox1 = QHBoxLayout()
CpsQHBox1.setSpacing(0)
CpsQHBox1.addStretch()
CpsQHBox1.addWidget(CpsPipConfigLabel)
CpsQHBox1.addWidget(CpsPipConfigComboBox)
CpsQHBox1.addStretch()
vbox.addLayout(CpsQHBox1)
vbox.addStretch()
CpsQHBox2 = QHBoxLayout()
CpsQHBox2.setSpacing(0)
CpsQHBox2.addStretch()
CpsQHBox2.addSpacing(20)
CpsQHBox2.addWidget(CpsTotalTeethLabel)
CpsQHBox2.addWidget(CpsTotalTeethEdit)
CpsQHBox2.addStretch()
CpsQHBox2.addWidget(CpsMissingTeethLabel)
CpsQHBox2.addWidget(CpsMissingTeethEdit)
CpsQHBox2.addStretch()
vbox.addLayout(CpsQHBox2)
vbox.addStretch()
CpsQHBox3 = QHBoxLayout()
CpsQHBox3.setSpacing(0)
CpsQHBox3.addStretch()
CpsQHBox3.addWidget(CpsChannel)
CpsQHBox3.addWidget(CpsChannelComboBox)
CpsQHBox3.addStretch()
vbox.addLayout(CpsQHBox3)
vbox.addStretch()
self.setLayout(vbox)
self.setGeometry(200, 100, 300, 300)
self.setWindowTitle('Steady state data processing')
self.setWindowIcon(QIcon('duty_vs_suction_map_sum.png'))
self.setAutoFillBackground(True)
p = self.palette()
p.setColor(self.backgroundRole(), QColor(255,250,100))
# p.setColor(self.backgroundRole(), Qt.blue)
self.setPalette(p)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I suggest you set the widgets up and place them at the beginning like you have them, but set them invisible. Then make a method that sets the appropriate widgets visible based on the qcombobox's current text and connect it to the qcombobox's activated signal.
You will also need to add self in front of almost every object so that it can be referred to from other methods.
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# setup code here...
self.CpsTotalTeethEdit.setVisible(False)
self.CpsTotalTeethLabel.setVisible(False)
self.CpsPipConfigComboBox.activated.connect(self.setup_total_teeth)
self.show()
def setup_widgets(self):
if self.CpsPipConfigComboBox.currentText() == "Multi pip":
self.CpsTotalTeethLabel.setVisible(True)
self.CpsTotalTeethEdit.setVisible(True)
By setting the items invisible instead of adding them with this method, you can also set them to be not visible when the cobobox's position is not for them.
I have a list which is generated based on user-input.
I am trying to display this list in a QMessageBox. But, I have no way of knowing the length of this list. The list could be long.
Thus, I need to add a scrollbar to the QMessageBox.
Interestingly, I looked everywhere, but I haven’t found any solutions for this.
Below is, what I hope to be a “Minimal, Complete and Verifiable Example”, of course without the user input; I just created a list as an example.
I appreciate any advice.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class W(QWidget):
def __init__(self):
super().__init__()
self.initUi()
def initUi(self):
self.btn = QPushButton('Show Message', self)
self.btn.setGeometry(10, 10, 100, 100)
self.btn.clicked.connect(self.buttonClicked)
self.lst = list(range(2000))
self.show()
def buttonClicked(self):
result = QMessageBox(self)
result.setText('%s' % self.lst)
result.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = W()
sys.exit(app.exec_())
You can not add a scrollbar directly since the widget in charge of displaying the text is a QLabel. The solution is to add a QScrollArea. The size may be inadequate so a stylesheet has to be used to set minimum values.
class ScrollMessageBox(QMessageBox):
def __init__(self, l, *args, **kwargs):
QMessageBox.__init__(self, *args, **kwargs)
scroll = QScrollArea(self)
scroll.setWidgetResizable(True)
self.content = QWidget()
scroll.setWidget(self.content)
lay = QVBoxLayout(self.content)
for item in l:
lay.addWidget(QLabel(item, self))
self.layout().addWidget(scroll, 0, 0, 1, self.layout().columnCount())
self.setStyleSheet("QScrollArea{min-width:300 px; min-height: 400px}")
class W(QWidget):
def __init__(self):
super().__init__()
self.btn = QPushButton('Show Message', self)
self.btn.setGeometry(10, 10, 100, 100)
self.btn.clicked.connect(self.buttonClicked)
self.lst = [str(i) for i in range(2000)]
self.show()
def buttonClicked(self):
result = ScrollMessageBox(self.lst, None)
result.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = W()
sys.exit(app.exec_())
Output:
Here is another way to override the widgets behavior.
You can get references to the children of the widget by using 'children()'.
Then you can manipulate them like any other widget.
Here we add a QScrollArea and QLabel to the original widget's QGridLayout. We get the text from the original widget's label and copy it to our new label, finally we clear the text from the original label so it is not shown (because it is beside our new label).
Our new label is scrollable. We must set the minimum size of the scrollArea or it will be hard to read.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ScrollMessageBox(QMessageBox):
def __init__(self, *args, **kwargs):
QMessageBox.__init__(self, *args, **kwargs)
chldn = self.children()
scrll = QScrollArea(self)
scrll.setWidgetResizable(True)
grd = self.findChild(QGridLayout)
lbl = QLabel(chldn[1].text(), self)
lbl.setWordWrap(True)
scrll.setWidget(lbl)
scrll.setMinimumSize (400,200)
grd.addWidget(scrll,0,1)
chldn[1].setText('')
self.exec_()
class W(QWidget):
def __init__(self):
super(W,self).__init__()
self.btn = QPushButton('Show Message', self)
self.btn.setGeometry(10, 10, 100, 100)
self.btn.clicked.connect(self.buttonClicked)
self.message = ("""We have encountered an error.
The following information may be useful in troubleshooting:
1
2
3
4
5
6
7
8
9
10
Here is the bottom.
""")
self.show()
def buttonClicked(self):
result = ScrollMessageBox(QMessageBox.Critical,"Error!",self.message)
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = W()
sys.exit(app.exec_())
I am using a QVBox layout and there are two widgets and a dynamic layout 'layout2' in the layout. Widget1 is fixed on top Widget3 is fixed at the bottom and widget2 is dynamic widget. layout2 is deleted and added each time. The problem here is I am not able to position the widget3 at the bottom as layout2 layout is deleted Widget3 moves to the top. Below is the sample code.
class Screen(QWidget):
def __init__(self):
super(Screen, self).__init__()
self.main_layout = QVBoxLayout()
widget1 = QPushButton("Text1")
#self.widget2 = QWidget()
widget3 = QLabel("Text3")
self.widget2_layout = QHBoxLayout()
widget2_label = QLabel("text2")
self.widget2_layout.addWidget(widget2_label)
#self.widget2.setLayout(self.widget2_layout)
self.main_layout.addWidget(widget1,Qt.AlignTop)
self.main_layout.addLayout(self.widget2_layout)
self.main_layout.addWidget(widget3,Qt.AlignBottom)
widget1.clicked.connect(self.change_widget2)
self.setLayout(self.main_layout)
self.show()
def clearLayout(self,layout):
item = layout.takeAt(0)
while item:
w = item.widget()
if w:
w.deleteLater()
lay = item.layout()
if lay:
self.clearLayout(item.layout())
item = layout.takeAt(0)
def change_widget2(self):
self.clearLayout(self.widget2_layout)
self.widget2_layout = QHBoxLayout()
widget2_label = QLabel("text changed")
self.widget2_layout.addWidget(widget2_label)
self.main_layout.addLayout(self.widget2_layout)
app = QApplication(sys.argv)
Gui = Screen()
sys.exit(app.exec_())
I have tried addstretch, dummy additional layout and nothing worked.
If you only want to change the widget that is in the second position it is not necessary to delete create a new layout, it is only necessary to reuse it, in the following example we see how the widget is changing:
class Screen(QWidget):
def __init__(self):
super(Screen, self).__init__()
self.setLayout(QVBoxLayout())
widget1 = QPushButton("Text1", self)
widget3 = QLabel("Text3", self)
self.widget2_layout = QHBoxLayout()
self.change_widget2()
self.layout().addWidget(widget1)
self.layout().addLayout(self.widget2_layout)
self.layout().addWidget(widget3)
widget1.clicked.connect(self.change_widget2)
def clearLayout(self, layout):
item = layout.takeAt(0)
while item:
w = item.widget()
if w:
w.deleteLater()
lay = item.layout()
if lay:
self.clearLayout(item.layout())
item = layout.takeAt(0)
def change_widget2(self):
self.clearLayout(self.widget2_layout)
# change the widget.
import random
widgets = [QLabel, QLineEdit, QPushButton]
widget2 = widgets[random.randint(0, len(widgets)-1)]("widget2", self)
self.widget2_layout.addWidget(widget2)