Search code examples
pythonpyqtpyqt5qscrollareaqsizepolicy

Looking to fix size policy and how scrollArea shows in a tab


so I am trying to use PyQt5 to put a scroll area inside of a tab. Just playing around with a system that reads the serial port.

The issue I am having is that although I set a QVBoxLayout, the size does not seem to adjust to what I want it to be. It seems to be taking its minimum size and not adjusting.

I tried looking at the documentation on QSizePolicy on the website provided, but unfortunately it is all labeled as TODO.

https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qsizepolicy.html

I was wondering if anyone had some experience with this?

import sys
from PyQt5.QtWidgets import QMainWindow, QSizePolicy, QLabel, QGridLayout, QToolTip, QPlainTextEdit, QScrollArea, QApplication, QPushButton, QWidget, QAction, QTabWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtGui import *
from PyQt5.QtCore import pyqtSlot, QDateTime, Qt, pyqtSignal, QObject, QSize
import datetime
import serial
import serial.tools.list_ports

import threading


class FDSerial(QMainWindow):

    def __init__(self):

        super().__init__()

        self.initUI()

    def initUI(self):

        self.connected = False
        self.fd_line = ""

        newAct = QAction(QIcon('icn/001-file.png'), 'New', self)
        newAct.triggered.connect(self.init_sc)

        self.toolbar = self.addToolBar('New')
        self.toolbar.addAction(newAct)

        openAct = QAction(QIcon('icn/002-folder.png'), 'Open', self)
        self.toolbar.addAction(openAct)

        connectAct = QAction(QIcon('icn/003-pendrive.png'), 'Connect', self)
        connectAct.triggered.connect(self.find_port)
        self.toolbar.addAction(connectAct)

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        editMenu = menubar.addMenu('&Settings')
        toolsMenu = menubar.addMenu('&Tools')
        sessionMenu = menubar.addMenu('&Session')
        helpMenu = menubar.addMenu('&Help')

        self.statusBar().showMessage('Ready')

        self.table_widget = LayoutWidgets(self)
        self.setCentralWidget(self.table_widget)

        self.setGeometry(400, 400, 800, 600)
        self.setWindowTitle('FD Serial Demo')
        self.show()

        self.c = Communicate()
        self.c.serialStuff.connect(lambda: self.table_widget.add_row(self.fd_line))

    def init_sc(self):
        self.ser = serial.Serial()
        self.ser.baudrate = 115200
        self.is_connected = False
        self.tests_run = 0
        self.port_found = False

    def find_port(self):
        if self.is_connected is False:
            self.is_connected = True
        else:
            self.is_connected = False
        for port in serial.tools.list_ports.comports():
            if port.vid == 5824 and port.pid == 1155:
                self.ser.port = str(port.device)
                self.port_found = True
                print("Found")
        if self.port_found is False:
            print("Not found")
        x = threading.Thread(target=self.talk_module)
        x.start()

    def talk_module(self):
        self.ser.open()
        while self.is_connected is True:
            self.fd_line = self.ser.readline().decode()
            print(self.fd_line)
            self.c.serialStuff.emit()
        self.ser.close()


class Communicate(QObject):

    serialStuff = pyqtSignal()


class LayoutWidgets(QWidget):

    def __init__(self, parent):
        super(QWidget, self).__init__(parent)

        self.layout = QVBoxLayout(self)
        self.thisthat = 0

        self.mySizePolicy = QSizePolicy()
        self.mySizePolicy.setHorizontalStretch(1)
        self.mySizePolicy.setVerticalStretch(1)
        # self.mySizePolicy.setHeightForWidth(False)
        # self.mySizePolicy.setHorizontalPolicy(QSizePolicy.Maximum)
        # self.mySizePolicy.setVerticalPolicy(QSizePolicy.Maximum)

        self.tabs = QTabWidget()
        self.tab1 = QWidget()
        self.tab2 = QWidget()

        self.tabs.addTab(self.tab1, "Serial CANFD Interface")
        self.tabs.addTab(self.tab2, "Data Visualizer")
        self.tab1.layout = QVBoxLayout()
        self.tab2.layout = QVBoxLayout()

        self.scrollArea = QScrollArea(self.tab1)

        self.scrollArea.setWidgetResizable(True)

        # self.widget = QWidget()
        # self.scrollArea.setWidget(self.widget)

        self.layout_SArea = QVBoxLayout(self.scrollArea)
        self.layout_SArea.setSpacing(0)

        self.tab1.layout.addWidget(self.scrollArea)

        self.scrollArea.setSizePolicy(self.mySizePolicy)
        self.scrollArea.setStyleSheet("background-color:'#d3f3c8'")

        self.layout.addWidget(self.tabs)

        self.setLayout(self.layout)

        self.qtextbig = QPlainTextEdit()

        self.qtextbig.setSizePolicy(self.mySizePolicy)

        self.qtextbig.setReadOnly(False)
        self.layout_SArea.addWidget(self.qtextbig)

    def add_row(self, row):
        self.this = str(row)
        self.this2 = str(datetime.datetime.now().time())
        self.thisthat = self.thisthat + 1
        self.qtextbig.appendPlainText(self.this)
        self.qtextbig.appendPlainText(self.this2)
        self.qtextbig.appendPlainText(str(self.thisthat))


if __name__ == '__main__':

    app = QApplication(sys.argv)

    ex = FDSerial()

    sys.exit(app.exec_())

Here is how the GUI looks right now.  The scroll are is way too small!  It is in green.

Here is how the GUI looks right now. The scroll are is way too small! It is in green.

I am looking for the scroll area to take the whole tab size, and then adjust as I adjust the window size. Would appreciate any pointers.

Thanks!


Solution

  • The problem has nothing to do with the QSizePolicy, they are not really necessary. The problem is that you are not using layouts. I think that using the following instruction:

    self.tab1.layout = QVBoxLayout()
    self.tab2.layout = QVBoxLayout()
    

    add a layout to each tab, but it is only pointing out that there is a new property called layout that takes the value of the QVBoxLayout and that deletes the reference to the layout method of the tab, ie it only deletes the access to the tab1.layout() method, and doing it is a bad practice.

    Considering the above I have used the following code:

    # ...
    class LayoutWidgets(QWidget):
        def __init__(self, parent=None):
            super(QWidget, self).__init__(parent)
    
            layout = QVBoxLayout(self)
            self.thisthat = 0
    
            self.tabs = QTabWidget()
            layout.addWidget(self.tabs)
    
            self.tab1 = QWidget()
            self.tab2 = QWidget()
    
            self.tabs.addTab(self.tab1, "Serial CANFD Interface")
            self.tabs.addTab(self.tab2, "Data Visualizer")
    
            lay = QVBoxLayout(self.tab1)
            self.scrollArea = QScrollArea(widgetResizable=True)
            self.scrollArea.setStyleSheet("background-color:'#d3f3c8'")
            lay.addWidget(self.scrollArea)
    
            layout_SArea = QVBoxLayout(self.scrollArea)
            self.qtextbig = QPlainTextEdit(readOnly=False)
            layout_SArea.addWidget(self.qtextbig)
    
        def add_row(self, row):
            self.this = str(row)
            self.this2 = str(datetime.datetime.now().time())
            self.thisthat = self.thisthat + 1
            self.qtextbig.appendPlainText(self.this)
            self.qtextbig.appendPlainText(self.this2)
            self.qtextbig.appendPlainText(str(self.thisthat))
    # ...
    

    enter image description here