If you like DNray Forum, you can support it by - BTC: bc1qppjcl3c2cyjazy6lepmrv3fh6ke9mxs7zpfky0 , TRC20 and more...

 

Adjusting Window Size Based on Dynamic Widget Count

Started by worldtraveler, Oct 07, 2024, 12:31 AM

Previous topic - Next topic

worldtravelerTopic starter

Hey there, The crux of the issue is this: we've got a program sporting a main window with a QTabWidget. Each tab dynamically spawns labels and buttons using QGridLayout. When certain category buttons are clicked, the widget count on the form fluctuates. The catch? The window size should adjust (shrink or grow) in sync with the widget size.

The resize method can pull this off if a button is created and its signal connected, but calling this method manually doesn't budge the form's size. I doubt anyone wants to dig through the entire codebase, so I've included the class code. Maybe someone can spot my boo-boo, particularly in the set_geometry_app method.

class Create_Grid(MyApp):
    def __init__(self, parent, tab_active='', cut_col=False, cut_row=False):
        super(MyApp, self).__init__(parent)
        self.tab_active = tab_active
        self.cut_row = cut_row
        self.cut_col = cut_col
        self.root = parent
        self.root.setLayout(QVBoxLayout())
        self.generate_grid()

    def generate_grid(self):
        file_input, file_output = name_file(self.tab_active)
        data_, data_color = read_files.data_for_table(file_output, show_manager=self.cut_row, sp_group=self.cut_col)
        self.widget_grid = QWidget()
        self.widget_grid.setLayout(QGridLayout(spacing=4))
        for i, col_name in enumerate(data_):
            for j, val in enumerate(col_name):
                if not i:
                    # Header row - column names
                    obj_ = QPushButton(val, parent=self.widget_grid)
                    if col and self.cut_col:
                        obj_.setFixedSize(70, 30)
                    obj_.clicked.connect(self.set_geometry_app)
                elif i == 1:
                    # First column - names (directions)
                    obj_ = QPushButton(val, parent=self.widget_grid)
                    obj_.clicked.connect(self.mouse_click_manager)
                else:
                    # Other data fields
                    color_ = data_color[i][j]
                    obj_ = QLabel(f'{val:,.0f}'.replace(',', ' '), parent=self.widget_grid)
                    obj_.setAlignment(Qt.AlignCenter)
                    obj_.setStyleSheet(f"background-color: {color_}")
                self.widget_grid.layout().addWidget(obj_, j, i, alignment=Qt.Alignment())
        self.root.layout().addWidget(self.widget_grid)

    def del_all_my_widget(self):
        for i in reversed(range(self.widget_grid.layout().count())):
            widgetToRemove = self.widget_grid.layout().itemAt(i).widget()
            self.widget_grid.layout().removeWidget(widgetToRemove)
            widgetToRemove.setParent(None)

    def set_geometry_app(self):
        # Now, changing the size of the program window works as expected
        w_ = self.root.parentWidget().parentWidget().parentWidget()
        w_.resize(200, 300)

    def mouse_click_manager(self):
        sending_button = self.sender()
        _filter = sending_button.text().split('(')[0]
        self.cut_row = None if self.cut_row else _filter
        self.del_all_my_widget()
        self.generate_grid()
        # Now, calling set_geometry_app works!
        self.set_geometry_app()
  •  


Bhvzdamkybdd

This is a classic anti-pattern in Qt development. Instead of calling set_geometry_app manually, you should let the layout do its job. After updating the layout in mouse_click_manager, call w_.layout().updateGeometry() followed by w_.adjustSize().
This will ensure that the layout is updated and the window is resized correctly. Additionally, consider using a more dynamic approach to resizing, such as using a QScrollArea or a QSizePolicy that adapts to the content.
  •  

surekhabhardwaj

When debugging an issue, it's essential to create a minimal, complete, and verifiable example (MCVE) that isolates the problem. This isn't about copying and pasting a truncated version of your combat code; instead, you need to craft a proof-of-concept (POC) that demonstrates the issue. In your case, the problem lies not in fetching data from an external source, but in rendering and displaying that data.

To tackle this, create a simple mockup that, when triggered by a button click, generates various table configurations, such as 3x8, 8x3, 5x100, 100x5, 50x1000, and 1000x200. Then, display these tables in the desired format. This model should account for all relevant factors.

I still have a question, though: is the number of buttons dynamic, or is it fixed?
  •  

chabip99

Let me drop a minimal code snippet that nails the problem: we're talking about a PyQt5 app with a tabbed interface, sporting two widgets (Widget0 and Widget1) with varying grid layouts, and a main window that adjusts its size based on the current tab. Here's the gist:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QPushButton, QLabel, QTabWidget, QVBoxLayout, QStackedLayout
from PyQt5.QtCore import QSize

class CustomWidget(QWidget):
    def __init__(self, rows, columns):
        super().__init__(None)
        self.grid_layout = QGridLayout()
        self.grid_layout.addWidget(QPushButton("button"), 0, 0)
        for column in range(1, columns):
            self.grid_layout.addWidget(QPushButton(f"column{column - 1}"), 0, column)
        for row in range(1, rows):
            self.grid_layout.addWidget(QPushButton(f"row{row - 1}"), row, 0)
        for column in range(1, columns):
            for row in range(1, rows):
                self.grid_layout.addWidget(QLabel("label"), row, column)
        self.setLayout(self.grid_layout)

class TabWidget(QTabWidget):
    def __init__(self):
        super().__init__()
        self.currentChanged.connect(self.updateGeometry)

    def minimumSizeHint(self):
        return self.sizeHint()

    def sizeHint(self):
        layout_hint = self.findChild(QStackedLayout).currentWidget().sizeHint()
        tab_hint = self.tabBar().sizeHint()
        size = QSize(max(layout_hint.width(), tab_hint.width()), layout_hint.height() + tab_hint.height())
        return size

class Window(QWidget):
    def __init__(self):
        super().__init__(None)
        self.tabs = TabWidget()
        self.tabs.addTab(CustomWidget(4, 13), "tab0")
        self.tabs.addTab(CustomWidget(10, 4), "tab1")
        self.tabs.currentChanged.connect(self.adjustSize)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.tabs)
        self.setLayout(self.layout)
        self.adjustSize()

app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

I'd advocate for user-centric design principles. Hardcoding window sizes is a big no-no, as it can lead to poor UX. Users should have control over window dimensions, and the app should be responsive, adapting to different screen sizes. In this case, the app decides the window size based on the current tab's content, which might result in a less-than-optimal user experience.
  •  


If you like DNray forum, you can support it by - BTC: bc1qppjcl3c2cyjazy6lepmrv3fh6ke9mxs7zpfky0 , TRC20 and more...