Hello I have this code using python and pyqt5 which allows to display a graphical interface :
import sys
from PyQt5 import QtCore, QtWidgets
class TabPage(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
group = QtWidgets.QGroupBox('Monty Python')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(group)
grid = QtWidgets.QGridLayout(group)
grid.addWidget(QtWidgets.QLabel('Enter a name:'), 0, 0)
grid.addWidget(QtWidgets.QLabel('Choose a number:'), 0, 1)
grid.addWidget(QtWidgets.QLineEdit(), 1, 0)
grid.addWidget(QtWidgets.QComboBox(), 1, 1)
grid.addWidget(QtWidgets.QPushButton('Click Me!'), 1, 2)
grid.addWidget(QtWidgets.QSpinBox(), 2, 0)
grid.addWidget(QtWidgets.QPushButton('Clear Text'), 2, 2)
grid.addWidget(QtWidgets.QTextEdit(), 3, 0, 1, 3)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setToolTip('Add New Tab')
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_DialogYesButton))
self.tabs.setCornerWidget(button, QtCore.Qt.TopRightCorner)
button1 = QtWidgets.QToolButton()
button1.setToolTip('Remove')
button1.clicked.connect(self.addNewTab)
button1.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_BrowserStop))
self.tabs.setCornerWidget(button1, QtCore.Qt.TopRightCorner)
self.addNewTab()
def addNewTab(self):
text = 'Tab %d' % (self.tabs.count() + 1)
self.tabs.addTab(TabPage(self.tabs), text)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
When I execute my code I get this :
whereas I would like to get something like this :
How can I do to do this ?
Thank you a lot !
QTabWidget::setCornerWidget(QWidget *widget, Qt::Corner corner = Qt::TopRightCorner)
Any previously set corner widget is hidden. https://doc.qt.io/qt-5/qtabwidget.html#setCornerWidget
Try it:
import sys
from PyQt5 import QtCore, QtWidgets
class TabPage(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
group = QtWidgets.QGroupBox('Monty Python')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(group)
grid = QtWidgets.QGridLayout(group)
grid.addWidget(QtWidgets.QLabel('Enter a name:'), 0, 0)
grid.addWidget(QtWidgets.QLabel('Choose a number:'), 0, 1)
grid.addWidget(QtWidgets.QLineEdit(), 1, 0)
grid.addWidget(QtWidgets.QComboBox(), 1, 1)
grid.addWidget(QtWidgets.QPushButton('Click Me!'), 1, 2)
grid.addWidget(QtWidgets.QSpinBox(), 2, 0)
grid.addWidget(QtWidgets.QPushButton('Clear Text'), 2, 2)
grid.addWidget(QtWidgets.QTextEdit(), 3, 0, 1, 3)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget()
self.tabs.setTabsClosable(True) # +
self.tabs.tabCloseRequested.connect(self.qtabwidget_tabcloserequested) # +
self.tabs.currentChanged.connect(lambda: print(f'currentIndex->{self.tabs.currentIndex()}')) #+
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setFixedSize(20, 20) # +
button.setToolTip('Add New Tab')
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_DialogYesButton))
# self.tabs.setCornerWidget(button, QtCore.Qt.TopRightCorner)
button1 = QtWidgets.QToolButton()
button1.setFixedSize(20, 20) # +
button1.setToolTip('Remove')
button1.clicked.connect(self.removeTab) # removeTab
button1.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_BrowserStop))
# Any previously set corner widget is hidden.
# self.tabs.setCornerWidget(button1, QtCore.Qt.TopRightCorner) #
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.widget = QtWidgets.QWidget()
h_layout = QtWidgets.QHBoxLayout(self.widget)
h_layout.setContentsMargins(0, 0, 0, 0)
h_layout.addWidget(button)
h_layout.addWidget(button1)
self.tabs.setCornerWidget(self.widget, QtCore.Qt.TopRightCorner)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.addNewTab()
def addNewTab(self):
text = 'Tab %d' % (self.tabs.count() + 1)
self.tabs.addTab(TabPage(self.tabs), text)
#QtCore.pyqtSlot(int)
def qtabwidget_tabcloserequested(self, index):
# gets the widget
widget = self.tabs.widget(index)
# if the widget exists
if widget:
# removes the widget
widget.deleteLater()
# removes the tab of the QTabWidget
self.tabs.removeTab(index)
def removeTab(self):
print('def removeTab(self): print')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
Since the other answer does not explain the cause of the problem and its code is confusing, I will explain the error in detail.
The error is that there can only be one cornerWidget, if you set a second cornerWidget it will replace the previous one, therefore only one QToolButton is observed. If you want to show several widgets then you have to use a container like a QWidget and place the other widgets there.
# ...
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setToolTip("Add New Tab")
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogYesButton))
button1 = QtWidgets.QToolButton()
button1.setToolTip("Remove")
button1.clicked.connect(self.addNewTab)
button1.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_BrowserStop))
container = QtWidgets.QWidget()
container.setContentsMargins(0, 0, 0, 0)
lay = QtWidgets.QHBoxLayout(container)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(button)
lay.addWidget(button1)
self.tabs.setCornerWidget(container, QtCore.Qt.TopRightCorner)
self.addNewTab()
Related
How to arrange Widgets in QGridLayout as desired? For example, In My code,
I need to reduce the space between the " Country" and "State" Button
Place the Widgets in top left most Corner ( Country Button in Top left Most Corner- No Margins)
reduce the space between "country" and "District" Buttons
Place the "bank" Button to the centre of the "Town" Button
need/arrange layout as per attached image
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class GridLayout_Example(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Grid layout Example")
self.setGeometry(100,100,1600,600)
self.initUI()
self.layout()
self.show()
def initUI(self):
self.btn_country_63 = QPushButton()
self.btn_country_63.setText("Country")
self.btn_country_63.setObjectName("ob_btn_country_63")
self.btn_country_63.setProperty('group', "1")
self.btn_country_63.setFixedSize(100, 25)
self.btn_country_63.setStyleSheet("background-color:red;color:white")
self.btn_country_63.installEventFilter(self)
# self.btn_country_63.clicked.connect(self.btn_country_63_select)
self.btn_state_63 = QPushButton()
self.btn_state_63.setText("State")
self.btn_state_63.setObjectName("ob_btn_state_63")
self.btn_state_63.setProperty('group', "1")
self.btn_state_63.setFixedSize(100, 25)
self.btn_state_63.setStyleSheet("background-color:red;color:white")
self.btn_state_63.installEventFilter(self)
# self.btn_state_63.clicked.connect(self.btn_state_63_select)
self.btn_district_63 = QPushButton()
self.btn_district_63.setText("District")
self.btn_district_63.setObjectName("ob_btn_district_63")
self.btn_district_63.setProperty('group', "1")
self.btn_district_63.setFixedSize(100, 25)
self.btn_district_63.setStyleSheet("background-color:red;color:white")
self.btn_district_63.installEventFilter(self)
# self.btn_district_63.clicked.connect(self.btn_district_63_select)
self.btn_town_63 = QPushButton()
self.btn_town_63.setText("Town")
self.btn_town_63.setObjectName("ob_btn_town_63")
self.btn_town_63.setProperty('group', "1")
self.btn_town_63.installEventFilter(self)
self.btn_town_63.setFixedSize(60, 80)
self.btn_town_63.setStyleSheet("background-color:red;color:white")
# self.btn_town_63.clicked.connect(self.btn_town_63_select)
self.btn_bank_63 = QPushButton()
self.btn_bank_63.setText("Bank")
self.btn_bank_63.setObjectName("ob_btn_bank_63")
self.btn_bank_63.setProperty('group', "1")
self.btn_bank_63.installEventFilter(self)
self.btn_bank_63.setFixedSize(100, 25)
# self.btn_bank_63.clicked.connect(self.btn_bank_63_select)
def layout(self):
self.layout_main_toppannel_2_right = QGridLayout()
self.frame_main_toppannel_2_right = QFrame()
self.frame_main_toppannel_2_right.setProperty('color', "2")
self.frame_main_toppannel_2_right.setFixedHeight(100)
self.frame_main_toppannel_2_right.setStyleSheet("background-color: lightgreen")
self.layout_main_toppannel_2_right = QGridLayout(self.frame_main_toppannel_2_right)
self.container63 = QWidget()
self.container63_box = QGridLayout(self.container63)
self.container63_box.addWidget(self.btn_country_63, 0, 0, Qt.AlignTop)
self.container63_box.addWidget(self.btn_district_63, 0, 1, Qt.AlignTop)
self.container63_box.addWidget(self.btn_state_63, 1, 0, Qt.AlignTop)
self.container63_box.addWidget(self.btn_town_63, 0, 3)
self.container63_box.addWidget(self.btn_bank_63,0,4)
self.container63_box.setColumnStretch(0,4)
self.container63_box.setRowStretch(0,2)
self.stackitem = QStackedWidget()
self.stackitem.addWidget(self.container63)
self.layout_main_toppannel_2_right.addWidget(self.stackitem)
self.main_layout = QHBoxLayout()
self.main_layout.addWidget(self.frame_main_toppannel_2_right)
self.setLayout(self.main_layout)
def main():
app = QApplication(sys.argv)
mainwindow = GridLayout_Example()
mainwindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
In a previous post I explained in detail the operation of the QGridLayout so in this post I will only detail the most outstanding.
To remove the space between the items you must use setSpacing(0) (you can also use setHorizontalSpacing(0) and setVerticalSpacing(0)).
To make the elements be pushed to the left you can set a stretch to the imaginary fifth column.
I have also modified the height of "Town" since it does not match the sum height of the 3 elements, forcing spaces to appear.
I also calculated the height of the green container after setting the items to match the space needed and there are no spaces.
Finally note that some margins cannot be changed by code since they are implemented by the native QStyles of the OS, to avoid that I impose that the style fusion be used.
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class GridLayout_Example(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.build_widgets()
self.build_layout()
self.resize(640, 480)
def build_widgets(self):
self.btn_country_63 = QPushButton(
text="Country", styleSheet="background-color:red;color:white"
)
self.btn_country_63.setFixedSize(100, 25)
self.btn_state_63 = QPushButton(
text="State", styleSheet="background-color:red;color:white"
)
self.btn_state_63.setFixedSize(100, 25)
self.btn_district_63 = QPushButton(
text="District", styleSheet="background-color:red;color:white"
)
self.btn_district_63.setFixedSize(100, 25)
self.btn_town_63 = QPushButton(
text="Town", styleSheet="background-color:red;color:white"
)
self.btn_town_63.setFixedSize(60, 75)
self.btn_bank_63 = QPushButton(
text="Bank", styleSheet="background-color:red;color:white"
)
self.btn_bank_63.setFixedSize(100, 25)
self.btn_zip_63 = QPushButton(
text="Zip Code", styleSheet="background-color:red;color:white"
)
self.btn_zip_63.setFixedSize(100, 25)
def build_layout(self):
self.container = QFrame()
self.container.setStyleSheet("background-color: lightgreen")
grid_layout = QGridLayout(self.container)
grid_layout.addWidget(self.btn_country_63, 0, 0, 1, 1)
grid_layout.addWidget(self.btn_state_63, 1, 0, 1, 1)
grid_layout.addWidget(self.btn_district_63, 0, 1, 1, 1)
grid_layout.addWidget(self.btn_town_63, 0, 2, 3, 1)
grid_layout.addWidget(self.btn_bank_63, 1, 3, 1, 1)
grid_layout.addWidget(self.btn_zip_63, 2, 4, 1, 1)
grid_layout.setColumnStretch(5, 1)
grid_layout.setSpacing(0)
grid_layout.setContentsMargins(0, 0, 0, 0)
lay = QVBoxLayout(self)
lay.addWidget(self.container)
self.container.setFixedHeight(self.container.sizeHint().height())
def main():
app = QApplication(sys.argv)
print("default-style: ", app.style().metaObject().className())
app.setStyle("fusion")
mainwindow = GridLayout_Example()
mainwindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I have created a button who creates buttons in a vertical layout, called "elementos". In each row, there is a label and a X button who should delete the row. There is a list with the text of the label called "puntos" MRE:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QOpenGLWidget
import sys
from sys import argv, exit
class Renderizador(QOpenGLWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.puntos = []
self.num_elementos = 0
def borrar_punto(self):
self.puntos.pop()
ui.elementos.removeWidget(self.name)
ui.elementos.removeWidget(self.borrar)
self.name.deleteLater()
self.borrar.deleteLater()
self.num_elementos -= 1
def crear_punto(self):
do = ui.valor_do.value()
cota = ui.valor_cota.value()
alejamiento = ui.valor_alej.value()
self.puntos.append((ui.nombre.toPlainText(), do, cota, alejamiento))
self.name = QtWidgets.QLabel()
self.name.setText(ui.nombre.toPlainText() + "({}, {}, {})".format(do, cota, alejamiento))
self.borrar = QtWidgets.QPushButton()
self.borrar.setText("X")
self.borrar.clicked.connect(lambda: self.borrar_punto())
ui.elementos.addWidget(self.name, self.num_elementos, 0, 1, 1)
ui.elementos.addWidget(self.borrar, self.num_elementos, 1, 1, 1)
self.num_elementos += 1
self.update()
class UiVentana:
def __init__(self):
ventana.resize(1500, 1000)
ventana.setLayoutDirection(QtCore.Qt.LeftToRight)
ventana.setFixedSize(ventana.size())
self.widget_central = QtWidgets.QWidget(ventana)
self.gridLayoutWidget = QtWidgets.QWidget(self.widget_central)
self.gridLayoutWidget.setGeometry(QtCore.QRect(1000, 60, 280, 50))
self.Vistas = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.Visor = Renderizador(self.widget_central)
self.Visor.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
self.gridLayoutWidget_2 = QtWidgets.QWidget(self.widget_central)
self.gridLayoutWidget_2.setGeometry(QtCore.QRect(1004, 105, 300, 400))
self.Punto = QtWidgets.QGridLayout(self.gridLayoutWidget_2)
self.Punto.setContentsMargins(5, 5, 5, 5)
self.Punto.setVerticalSpacing(4)
self.texto_nombre = QtWidgets.QLabel(self.gridLayoutWidget_2)
self.texto_nombre.setText("Nombre:")
self.Punto.addWidget(self.texto_nombre, 3, 0, 1, 1)
self.crear_punto = QtWidgets.QPushButton(self.gridLayoutWidget_2)
self.crear_punto.setText("Crear")
self.crear_punto.clicked.connect(self.Visor.crear_punto)
self.Punto.addWidget(self.crear_punto, 3, 2, 1, 1)
self.texto_cota = QtWidgets.QLabel(self.gridLayoutWidget_2)
self.nombre = QtWidgets.QTextEdit(self.gridLayoutWidget_2)
self.nombre.setMaximumSize(QtCore.QSize(100, 24))
self.Punto.addWidget(self.nombre, 3, 1, 1, 1)
self.scroll = QtWidgets.QScrollArea()
self.scroll.setWidgetResizable(True)
self.scroll_widget = QtWidgets.QWidget()
self.scroll_widget.resize(200, 300)
self.elementos_widget = QtWidgets.QWidget()
self.vbox = QtWidgets.QVBoxLayout(self.scroll_widget)
self.vbox.setContentsMargins(0, 0, 0, 0)
self.vbox.addWidget(self.elementos_widget)
self.vbox.addStretch()
self.elementos = QtWidgets.QGridLayout()
self.elementos_widget.setLayout(self.elementos)
self.scroll.setWidget(self.scroll_widget)
self.scroll_widget.setLayout(self.elementos)
self.scroll.setWidget(self.scroll_widget)
self.Punto.addWidget(self.scroll, 4, 0, 1, 3)
self.valor_do = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
self.Punto.addWidget(self.valor_do, 2, 0, 1, 1)
self.valor_alej = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
self.Punto.addWidget(self.valor_alej, 2, 1, 1, 1)
self.valor_cota = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
self.Punto.addWidget(self.valor_cota, 2, 2, 1, 1)
ventana.setCentralWidget(self.widget_central)
ventana.show()
def except_hook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
if __name__ == "__main__":
app = QtWidgets.QApplication(argv)
ventana = QtWidgets.QMainWindow()
ui = UiVentana()
sys.excepthook = except_hook
exit(app.exec_())
The pop statement should delete from the list the corresponding info about the label. I get an out of index range error, but i get a "RuntimeError: wrapped C/C++ object of type QLabel has been deleted" error if I pop the last statement.
Full code: https://github.com/Jaime02/Proyecto-de-investigacion-2019-Dibujo-tecnico/blob/experimental/error (Lines 120-140)
Edit: The delete button only works once if two or more rows are created
Your code has at least the following errors:
If you are going to create an object within a loop, do not make it a member, for example in your case self.name and self.borrar are variables that will always point to the last QLabel and QPushButton, so when you delete only the last row will be deleted in a single occasion.
Your code is very messy since they are modifying variables in places where they are not due as they could cause problems such as tracking errors, for example the variable window and ui.
Considering the above I have rewritten your code implementing the logic of passing the widgets and deleting the widgets directly.
import sys
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.puntos = []
self.visor = QtWidgets.QOpenGLWidget()
self.valor_do = QtWidgets.QSpinBox()
self.valor_cota = QtWidgets.QSpinBox()
self.valor_alej = QtWidgets.QSpinBox()
self.texto_nombre = QtWidgets.QLabel("Nombre")
self.nombre = QtWidgets.QLineEdit()
self.crear_punto = QtWidgets.QPushButton("Crear", clicked=self.crear_punto)
elementos_widget = QtWidgets.QWidget()
scroll_widget = QtWidgets.QWidget()
scroll = QtWidgets.QScrollArea(widgetResizable=True)
scroll.setWidget(scroll_widget)
vbox = QtWidgets.QVBoxLayout(scroll_widget)
vbox.addWidget(elementos_widget)
vbox.addStretch()
self.elementos = QtWidgets.QGridLayout(elementos_widget)
grid_layout = QtWidgets.QGridLayout()
grid_layout.addWidget(self.valor_do, 0, 0)
grid_layout.addWidget(self.valor_cota, 0, 1)
grid_layout.addWidget(self.valor_alej, 0, 2)
grid_layout.addWidget(self.texto_nombre, 1, 0)
grid_layout.addWidget(self.nombre, 1, 1)
grid_layout.addWidget(self.crear_punto, 1, 2)
grid_layout.addWidget(scroll, 2, 0, 1, 3)
for i in range(3):
grid_layout.setColumnStretch(i, 1)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QHBoxLayout(central_widget)
lay.addWidget(self.visor, stretch=1)
lay.addLayout(grid_layout)
self.resize(1280, 960)
def crear_punto(self):
do = self.valor_do.value()
cota = self.valor_cota.value()
alejamiento = self.valor_alej.value()
name = QtWidgets.QLabel(
"{}({}, {}, {})".format(self.nombre.text(), do, cota, alejamiento)
)
punto = (self.nombre.text(), do, cota, alejamiento)
borrar = QtWidgets.QPushButton("X")
wrapper = partial(self.borrar_punto, (name, borrar), punto)
borrar.clicked.connect(wrapper)
row = self.elementos.rowCount()
self.elementos.addWidget(name, row, 0)
self.elementos.addWidget(borrar, row, 1)
self.puntos.append(punto)
def borrar_punto(self, widgets, punto):
if self.puntos:
name, borrar = widgets
name.deleteLater()
borrar.deleteLater()
self.puntos.remove(punto)
print(self.puntos)
def except_hook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
sys.excepthook = except_hook
w = UiVentana()
w.show()
exit(app.exec_())
I have the below code:
import os
from functools import partial
import numpy as np
from PyQt5 import QtCore, QtWidgets
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(500, 500)
self.setWindowFlags(
self.windowFlags() | QtCore.Qt.MSWindowsFixedSizeDialogHint
)
self.setWindowTitle("nCode analysis set-up")
self.wait_window = WaitWindow()
thread = QtCore.QThread(self)
thread.start()
self.m_worker = Worker()
self.m_worker.moveToThread(thread)
self.m_worker.new_content_signal.connect(self.get_content)
# Creating the top level grid layout
mainGrid = QtWidgets.QGridLayout(self)
self.analysis_type_label = QtWidgets.QLabel(self)
self.analysis_type_label.setText("Type of analysis")
mainGrid.addWidget(self.analysis_type_label, 0, 0)
self.analysis_type_combo = QtWidgets.QComboBox(self)
self.analysis_type_combo.addItems(["Fatigue", "Proof plus fatigue"])
mainGrid.addWidget(self.analysis_type_combo, 0, 1, 1, 2)
self.load_deck_type_label = QtWidgets.QLabel(self)
self.load_deck_type_label.setText("Type of fatigue deck")
mainGrid.addWidget(self.load_deck_type_label, 1, 0)
self.load_deck_type_combo = QtWidgets.QComboBox(self)
self.load_deck_type_combo.addItems(
["Regen braking", "No regen braking"]
)
mainGrid.addWidget(self.load_deck_type_combo, 1, 1, 1, 2)
self.analysis_engine_type_label = QtWidgets.QLabel(self)
self.analysis_engine_type_label.setText("Analysis Engine")
mainGrid.addWidget(self.analysis_engine_type_label, 2, 0)
self.analysis_engine_type_combo = QtWidgets.QComboBox(self)
self.analysis_engine_type_combo.addItems(["EN analysis", "SN analysis"])
mainGrid.addWidget(self.analysis_engine_type_combo, 2, 1, 1, 2)
# Creating a scrolable area to accommodate for a large number of components with possible lenghty names
self.scrollArea = QtWidgets.QScrollArea(self)
# The line below is absolutely required to make the scrollable area work.
self.scrollArea.setWidgetResizable(True)
mainGrid.addWidget(self.scrollArea, 3, 0, 1, 3)
self.secondaryWidget = QtWidgets.QWidget()
self.scrollArea.setWidget(self.secondaryWidget)
self.secondaryGrid = QtWidgets.QGridLayout(self.secondaryWidget)
self.createDCL = QtWidgets.QPushButton(self)
self.createDCL.setText("Create DCL")
mainGrid.addWidget(self.createDCL, 4, 0, 1, 3)
def start_task(self):
if not os.path.exists("loading_database.db"):
QtWidgets.QMessageBox.information(
None,
"Loading database missing",
"Loading database has not been found. Creation of a new one will be attempted",
)
# self.loadingDatabaseCreator()
QtWidgets.QMessageBox.information(
None, "Successful", "Loading database succesfully created"
)
filePath, _ = QtWidgets.QFileDialog.getOpenFileName(
None, "Select input model", "", "Input deck (*.inp)", "*.inp"
)
if filePath:
self.wait_window.show()
self.m_worker.finished.connect(self.wait_window.close)
wrapper = partial(self.m_worker.read_file, filePath)
# Launch the task in a reasonable time for the window to show
QtCore.QTimer.singleShot(100, wrapper)
w.show()
self.wait_window.raise_()
self.wait_window.activateWindow()
#QtCore.pyqtSlot(int, str)
def get_content(self, i, content):
label = QtWidgets.QLabel("{} material".format(content))
linedit = QtWidgets.QLineEdit(placeholderText="Drop material name here")
linedit.setFixedWidth(150)
button = QtWidgets.QPushButton("Pick material")
self.secondaryGrid.addWidget(label, 2 + i, 0)
self.secondaryGrid.addWidget(linedit, 2 + i, 1)
self.secondaryGrid.addWidget(button, 2 + i, 2)
class WaitWindow(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Info")
self.resize(600, 200)
layout = QtWidgets.QVBoxLayout(self)
self.message = QtWidgets.QLabel()
self.message.setFixedWidth(550)
self.message.setText("Please wait while input file is being read")
layout.addWidget(self.message)
class Worker(QtCore.QObject):
finished = QtCore.pyqtSignal()
new_content_signal = QtCore.pyqtSignal(int, str)
#QtCore.pyqtSlot(str)
def read_file(self, fileName):
i = 0
collector_array = []
with open(fileName, "r") as model_file_obj:
for line in model_file_obj.readlines():
if "*ELEMENT," in line and "DCOUP3D" not in line:
t = line.split("ELSET=")[1][:-1]
if t not in collector_array:
self.new_content_signal.emit(i, t)
QtCore.QThread.msleep(10)
collector_array.append(t)
i += 1
self.finished.emit()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWidget()
w.start_task()
sys.exit(app.exec_())
It looks like a lot of code but the MainWidget is initialized first. After the initialization the MainWidget function of start_task is called which show's the WaitWindow QDialog, asks the user for the input file which it starts parsing and then shows the MainWidget QWidget window. While this is not too bad I would like the user to not see the MainWidget window until the file has finished parsing and the WaitWindow is closed. Any ideas?
I do not understand why you use w.show(), remove it.
Going to the problem, if you want the MainWidget to show after executing the task, you just have to connect the show method to the finished signal
# ...
self.m_worker.finished.connect(self.wait_window.close)
self.m_worker.finished.connect(self.show)
# ...
Let's say I have an application with a number of QGroupBoxes like so:
import sys
from PyQt4 import QtGui, QtCore
class Main(QtGui.QWidget):
# pylint: disable=too-many-statements
def __init__(self, main):
super(Main, self).__init__()
self.grid_layout = QtGui.QGridLayout()
self.line_edit = QtGui.QLineEdit()
self.grid_layout.addWidget(self.create_settings_group(), 0, 0, 2, 1)
self.push_button = QtGui.QPushButton("go", self)
self.grid_layout.addWidget(self.create_controls_group(), 0, 1)
self.setLayout(self.grid_layout)
main.setCentralWidget(self)
def create_settings_group(self):
group_box_settings = QtGui.QGroupBox(self)
group_box_settings.setTitle("group1")
grid = QtGui.QGridLayout()
grid.addWidget(self.line_edit, 0, 0)
group_box_settings.setLayout(grid)
return group_box_settings
def create_controls_group(self):
group_box_settings = QtGui.QGroupBox(self)
group_box_settings.setTitle("group2")
grid = QtGui.QGridLayout()
grid.addWidget(self.push_button, 0, 0, 1, 2)
group_box_settings.setLayout(grid)
return group_box_settings
class GUI(QtGui.QMainWindow):
def __init__(self):
super(GUI, self).__init__()
self.ui = Main(self)
self.show()
app = QtGui.QApplication(sys.argv)
ex = GUI()
app.exec_()
When I open my simple application I see that the cursor is blinking in the line edit. But I just want the push button in another group box to be highlighted and to have enter press connected to it? how do I do that? using self.push_button.setFocus() doesn't do anything.
You can try setting the button Default property:
self.push_button.setDefault(True)
self.push_button.setFocus()
You have to set the focus a moment after showing up for it you can use a QTimer::singleShot() or QMetaObject::invokeMethod():
1. QTimer::singleShot()
...
self.push_button = QtGui.QPushButton("go", self)
self.grid_layout.addWidget(self.create_controls_group(), 0, 1)
self.push_button.setDefault(True)
QtCore.QTimer.singleShot(0, self.push_button.setFocus)
2. QMetaObject::invokeMethod()
...
self.push_button = QtGui.QPushButton("go", self)
self.grid_layout.addWidget(self.create_controls_group(), 0, 1)
self.push_button.setDefault(True)
QtCore.QMetaObject.invokeMethod(self.push_button,
"setFocus",
QtCore.Qt.QueuedConnection)
In my program some comboboxes (QComboBox) were used to make several settings. Sometimes its necessary not only to know the item the user selected but also the item which was selected previously in the combobox.
Well, transferring the new selection is quite easy:
self.MyCombobox.activated[str].connect(self.ComboChange)
But how do i manage to not only passing the newly selected but also the previous item to a function when the index changed?
My compromise solution would be to manually set a variable for every combobox which stores the last selected value so that it can be accessed when the selction has changed. But considering that i have a lot of Comboboxes this would be quite prone to errors until some boxes could be updated on different ways.
Thanks in advance for your help
A minimal working example:
import sys
from PyQt5.QtWidgets import ( QApplication, QWidget, QGridLayout, QComboBox,
QLabel)
class BaseWidget(QWidget):
def __init__(self):
super(BaseWidget, self).__init__()
self.setGeometry(300, 300, 300, 200)
# 2 Labels to display the new and the old item after selection
self.LabelOldItem = QLabel(self)
self.LabelNewItem = QLabel(self)
self.MyCombobox = QComboBox(self)
self.MyCombobox.addItems(['Item 1', 'Item 2', 'Item 3', 'Item 4'])
self.MyCombobox.activated[str].connect(self.ComboChange)
grid = QGridLayout()
grid.addWidget(self.MyCombobox, 0, 0, 1, 1)
grid.addWidget(self.LabelOldItem, 1, 0, 1, 1)
grid.addWidget(self.LabelNewItem, 2, 0, 1, 1)
self.setLayout(grid)
def ComboChange(self, newitem):
self.LabelOldItem.setText('Previous Selection: ') # <- crucial point
# How can i transfer both, not only the new item but also the previous
# item of the combobox when it gets activated?
self.LabelNewItem.setText('New Selection: <b>' + newitem + '</b>')
if __name__ == '__main__':
app = QApplication(sys.argv)
pyqtComboExample = BaseWidget()
pyqtComboExample.show()
sys.exit(app.exec_())
A possible solution is to create a custom QComboBox:
import sys
from PyQt5 import QtCore, QtWidgets
class ComboBox(QtWidgets.QComboBox):
new_signal = QtCore.pyqtSignal(str, str)
def __init__(self, parent=None):
super(ComboBox, self).__init__(parent)
self.lastSelected = ""
self.activated[str].connect(self.onActivated)
def onActivated(self, text):
self.new_signal.emit(self.lastSelected, text)
self.lastSelected = text
class BaseWidget(QtWidgets.QWidget):
def __init__(self):
super(BaseWidget, self).__init__()
self.setGeometry(300, 300, 300, 200)
# 2 Labels to display the new and the old item after selection
self.LabelOldItem = QtWidgets.QLabel()
self.LabelNewItem = QtWidgets.QLabel()
self.MyCombobox = ComboBox()
self.MyCombobox.addItems(['Item 1', 'Item 2', 'Item 3', 'Item 4'])
self.MyCombobox.new_signal.connect(self.ComboChange)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.MyCombobox, 0, 0, 1, 1)
grid.addWidget(self.LabelOldItem, 1, 0, 1, 1)
grid.addWidget(self.LabelNewItem, 2, 0, 1, 1)
def ComboChange(self, lastitem, newitem):
self.LabelOldItem.setText('Previous Selection: <b>{}</b>'.format(lastitem))
self.LabelNewItem.setText('New Selection: <b>{}</b>'.format(newitem))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
pyqtComboExample = BaseWidget()
pyqtComboExample.show()
sys.exit(app.exec_())
Another possible solution is to use sender() to get the QComboBox used, and save the old item in a property:
import sys
from PyQt5 import QtCore, QtWidgets
class BaseWidget(QtWidgets.QWidget):
def __init__(self):
super(BaseWidget, self).__init__()
self.setGeometry(300, 300, 300, 200)
# 2 Labels to display the new and the old item after selection
self.LabelOldItem = QtWidgets.QLabel()
self.LabelNewItem = QtWidgets.QLabel()
self.MyCombobox = QtWidgets.QComboBox()
self.MyCombobox.addItems(['Item 1', 'Item 2', 'Item 3', 'Item 4'])
self.MyCombobox.activated[str].connect(self.ComboChange)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.MyCombobox, 0, 0, 1, 1)
grid.addWidget(self.LabelOldItem, 1, 0, 1, 1)
grid.addWidget(self.LabelNewItem, 2, 0, 1, 1)
def ComboChange(self, newitem):
combo = self.sender()
lastitem = combo.property("lastitem")
self.LabelOldItem.setText('Previous Selection: <b>{}</b>'.format(lastitem))
self.LabelNewItem.setText('New Selection: <b>{}</b>'.format(newitem))
combo.setProperty("lastitem", newitem)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
pyqtComboExample = BaseWidget()
pyqtComboExample.show()
sys.exit(app.exec_())