0

I send the whole code of my small application (it needs ZeroTier-One installed to be run). QMainWindow centralWidget contains essentially a QTableView (inside a QGroupBox).

My aim is to resize the whole QMainWindow to fit contents snugly.

This code seems to work correctly vertically (almost correctly: I didn't find a way to shrink table beyond a certain limit), but horizontally cuts table roughly in half.

This is what I get:

enter image description here

... and this is what I would like to have:

enter image description here

(getting rid of blank space at the bottom would be even better, but I suspect this would be another, possibly unrelated, question)

I already perused several answers including: Resize window to fit content and Resize QMainWindow to minimal size after content of layout changes

What am I missing?

import json
import os
import sys
import typing

from PyQt6.QtCore import Qt, QAbstractTableModel, pyqtSlot, QModelIndex, QSettings, QTimer
from PyQt6.QtWidgets import QApplication, QMainWindow, QInputDialog, QHeaderView
from PyQt6.uic import loadUi
from urllib3 import PoolManager


class NetworksModel(QAbstractTableModel):
    class Column:
        def __init__(self, tag, header):
            self.tag = tag
            self.header = header

        def data(self, item, role):
            if role == Qt.ItemDataRole.DisplayRole:
                return str(item[self.tag])

    class CBColumn(Column):
        def data(self, item, role):
            if role == Qt.ItemDataRole.CheckStateRole:
                return Qt.CheckState.Checked if item[self.tag] else Qt.CheckState

    class LiColumn(Column):
        def data(self, item, role):
            if role == Qt.ItemDataRole.DisplayRole:
                return '\n'.join(item[self.tag])

    columns = [
        Column('name', 'Name'),
        Column('id', 'NwID'),
        Column('status', 'Status'),
        Column('type', 'Type'),
        LiColumn('assignedAddresses', 'Addresses'),
        CBColumn('allowDNS', 'DNS')
    ]

    def __init__(self):
        super().__init__()
        self._data = []

    def data(self, index: QModelIndex, role: int = ...) -> typing.Any:
        r = index.row()
        c = index.column()
        if r < len(self._data):
            e: dict = self._data[r]
            return self.columns[c].data(e, role)

    def rowCount(self, parent: QModelIndex = ...) -> int:
        return len(self._data)

    def columnCount(self, parent: QModelIndex = ...) -> int:
        return len(self.columns)

    def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any:
        if role == Qt.ItemDataRole.DisplayRole:
            if orientation == Qt.Orientation.Horizontal:
                return self.columns[section].header

    def set(self, data):
        self.beginResetModel()
        self._data = data
        self.endResetModel()

    def flags(self, index: QModelIndex) -> Qt.ItemFlag:
        f = super().flags(index)
        # c = index.column()
        # i = self.columns[c]
        # if i['formatter'] == bool:
        #     f |= Qt.ItemFlag.ItemIsUserCheckable
        return f


class MainWindow(QMainWindow):
    def __init__(self, settings):
        super().__init__()
        loadUi("mainwindow.ui", self)
        self.settings: QSettings = settings
        self.authtoken = self.settings.value('local/authtoken', None)
        self.pm = PoolManager()
        self.model = NetworksModel()
        self.networks.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
        self.networks.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
        self.networks.setModel(self.model)
        self.refresh_timer = QTimer()
        self.refresh_timer.timeout.connect(self.on_actionrefresh_triggered)
        if self.authtoken:
            self.refresh_timer.start(5*1000)
            self.on_actionrefresh_triggered()
        # self.main_layout.setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)

    @pyqtSlot()
    def on_actionauthtoken_triggered(self):
        authtoken, ok = QInputDialog.getText(self, 'enter authtoken', '24 alphanumerics')
        if ok:
            self.authtoken = authtoken
            # TODO: enable refreshing
            self.settings.setValue('local/authtoken', authtoken)
            self.settings.sync()
            self.on_actionrefresh_triggered()
            self.refresh_timer.start(5*1000)

    @pyqtSlot()
    def on_actionrefresh_triggered(self):
        if self.authtoken:
            r = self.pm.request('GET', 'http://localhost:9993/network', headers={'X-ZT1-AUTH': self.authtoken})
            if r.status == 200:
                j = r.data
                li = json.loads(j)
                self.model.set(li)
                self.trigger_resize()
            else:
                print(f'on_actionrefresh_triggered() request ERROR: {r.status}')
                self.refresh_timer.stop()
        else:
            print('on_actionrefresh_triggered() ERROR: missing authtoken')
            self.refresh_timer.stop()

    def trigger_resize(self):
        def _resize_me():
            self.adjustSize()
            # self.resize(self.minimumSizeHint())
        QTimer.singleShot(10, _resize_me)


if __name__ == '__main__':
    os.environ['DISPLAY'] = "localhost:10.0"
    app = QApplication(sys.argv)

    window = MainWindow(QSettings('Condarelli', 'ZT1'))
    window.show()

    # Start the event loop.
    exit(app.exec())

and mainwindow.ui:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="main_layout">
    <item>
     <widget class="QGroupBox" name="groupBox">
      <property name="sizePolicy">
       <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="title">
       <string>Networks:</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_2">
       <item>
        <widget class="QTableView" name="networks">
         <property name="sizePolicy">
          <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
           <horstretch>0</horstretch>
           <verstretch>0</verstretch>
          </sizepolicy>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>20</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuFile">
    <property name="title">
     <string>File</string>
    </property>
    <addaction name="actionauthtoken"/>
    <addaction name="actionrefresh"/>
   </widget>
   <addaction name="menuFile"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <action name="actionauthtoken">
   <property name="text">
    <string>authtoken</string>
   </property>
   <property name="toolTip">
    <string>set authtoken</string>
   </property>
  </action>
  <action name="actionrefresh">
   <property name="text">
    <string>refresh</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>
ZioByte
  • 2,690
  • 1
  • 32
  • 68
  • 1
    When scroll areas are involved, you should consider [`setSizeAdjustPolicy()`](https://doc.qt.io/qt-5/qabstractscrollarea.html#sizeAdjustPolicy-prop), but be aware that this might not be enough when the content of the scroll area change. – musicamante Apr 11 '22 at 10:29
  • @musicamante: adding `self.networks.setSizeAdjustPolicy(QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents)` to initialization did the trick. Residual imperfection is it leaves space for scrollbars even if they are not drawn. If you format this in a proper Answer I will accept it. – ZioByte Apr 11 '22 at 11:10

0 Answers0