johnashu
johnashu

Reputation: 2211

Refresh Layout and update data

I have the following pyqt code that displays a grid of data.

I am able to click on a letter, open a text input box and successfully save the data to the grid and print the updated data in the terminal.

I have tried update() and repaint() but they do nothing aand calling the createLayout() method throws an error of QLayout: Attempting to add QLayout "" to Widget "", which already has a layout

How can I refresh the grid/widget/layout to display the new data?

from PyQt5 import QtWidgets, QtCore, QtGui
import string

grid = [[["text", "GREEN"], 0, ["text", "RED"], 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]]

BTN = """QPushButton{{font-weight: bold; color: {};
        font-size: 14px; background-color: {}; 
        border-width: 2px; border-radius: 100px}}"""


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

        self.letter_count = dict(zip(string.ascii_uppercase, [x for x in range(26)]))

        self.initUI()
        self.createLayout()
        self.center_widget()

    def initUI(self):
        p = self.palette()
        gradient = QtGui.QLinearGradient(0, 0, 0, 400)
        gradient.setColorAt(0.0, QtGui.QColor('#f1f1f1'))
        gradient.setColorAt(1.0, QtGui.QColor('#00a1de'))
        p.setBrush(QtGui.QPalette.Window, QtGui.QBrush(gradient))
        self.setPalette(p)

    def createLayout(self):
        hlay = QtWidgets.QHBoxLayout(self)
        frameL = QtWidgets.QFrame()

        vlay = QtWidgets.QVBoxLayout(frameL)
        frame = QtWidgets.QFrame()
        frame.setObjectName("principal")
        frame.setStyleSheet("#principal{border: 2px solid white;}")
        hlay.addWidget(frameL)
        hlay.addWidget(frame)

        gridLayout = QtWidgets.QGridLayout(frame)

        h = 60  # height

        for i, row in enumerate(grid):
            letter = "{}".format(string.ascii_uppercase[i])
            frameButton = QtWidgets.QFrame()
            frameButton.setFixedHeight(h)
            frameButton.setContentsMargins(0, 0, 0, 0)
            lay = QtWidgets.QVBoxLayout(frameButton)
            button = QtWidgets.QPushButton(letter)
            button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
            button.clicked.connect(lambda x, letter=letter: self.populate_row(letter)) # Every button click returns 'J'
            lay.addWidget(button)
            vlay.addWidget(frameButton)
            for j, val in enumerate(row):
                gridButton = QtWidgets.QPushButton()
                gridButton.setFixedSize(h, h)
                if isinstance(val, list):
                    gridButton.setText(val[0])
                    if val[1] == "GREEN":
                        gridButton.setStyleSheet(BTN.format('white', 'green'))
                    elif val[1] == "RED":
                        gridButton.setStyleSheet(BTN.format('white', 'red'))
                else:
                    gridButton.setStyleSheet(BTN.format('black', 'white'))
                    gridButton.setText("{}".format(val))
                gridLayout.addWidget(gridButton, i, j)

        for ix in range(j + 1):
            label = QtWidgets.QLabel("{}".format(ix + 1))
            label.setAlignment(QtCore.Qt.AlignCenter)
            gridLayout.addWidget(label, i + 1, ix)
        vlay.addWidget(QtWidgets.QLabel())

    def center_widget(self):
        self.window().setGeometry(
            QtWidgets.QStyle.alignedRect(
                QtCore.Qt.LeftToRight,
                QtCore.Qt.AlignCenter,
                self.window().size(),
                QtWidgets.QApplication.desktop().availableGeometry())
        )

    def populate_row(self, letter):
        """ Populate the row from index 0 - end """
        print(letter)
        self.wi = QtWidgets.QWidget()
        self.wi.resize(660, 360)
        textBox = QtWidgets.QPlainTextEdit(self.wi)
        Rbtn = QtWidgets.QPushButton('Add To Row')
        Rbtn.clicked.connect(lambda: self.input_to_grid(textBox.toPlainText(), letter))       

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(textBox)
        layout.addWidget(Rbtn)

        Rbtn.move(600, 300)

        self.wi.setLayout(layout)

        self.wi.show()
        return self.wi

    def input_to_grid(self, text, letter):
        print(text, letter)
        lst = text.split(' ')
        for i, x in enumerate(lst):
            self.add_to_grid((letter, i + 1), x)
            print((letter, i + 1), x)
        self.print_grid()


    def add_to_grid(self, loc, item):
        x, y = self.translate(loc)
        grid[x][y] = [item] 

    def translate(self, loc):
        for k, v in self.letter_count.items():
            if loc[0].upper() == k:
                x = v
        y = int(loc[1]) - 1
        return x, y

    def print_grid(self):
        """ Print the grid prettier than normal.. """
        print('\n'.join(['\t'.join([str(c) for c in r]) for r in grid]))


if __name__ == '__main__':
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

Upvotes: 2

Views: 7038

Answers (1)

eyllanesc
eyllanesc

Reputation: 243993

It is a bad idea to replace the layouts or widgets since time and memory are wasted, the correct thing is to reuse, in this case I will create a list of lists that maps the buttons, so when you want to update some data in the grid the data will be updated in the buttons.

from PyQt5 import QtWidgets, QtCore, QtGui
import string

grid = [[["text", "GREEN"], 0, ["text", "RED"], 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0]]

BTN = """QPushButton{{font-weight: bold; color: {};
        font-size: 14px; background-color: {}; 
        border-width: 2px; border-radius: 100px}}"""


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

        self.letter_count = dict(zip(string.ascii_uppercase, [x for x in range(26)]))

        self.initUI()
        self.buttons = []
        self.createLayout()
        self.center_widget()

    def initUI(self):
        p = self.palette()
        gradient = QtGui.QLinearGradient(0, 0, 0, 400)
        gradient.setColorAt(0.0, QtGui.QColor('#f1f1f1'))
        gradient.setColorAt(1.0, QtGui.QColor('#00a1de'))
        p.setBrush(QtGui.QPalette.Window, QtGui.QBrush(gradient))
        self.setPalette(p)

    def createLayout(self):
        hlay = QtWidgets.QHBoxLayout(self)
        frameL = QtWidgets.QFrame()

        vlay = QtWidgets.QVBoxLayout(frameL)
        frame = QtWidgets.QFrame()
        frame.setObjectName("principal")
        frame.setStyleSheet("#principal{border: 2px solid white;}")
        hlay.addWidget(frameL)
        hlay.addWidget(frame)

        self.gridLayout = QtWidgets.QGridLayout(frame)

        h = 60  # height

        for i, row in enumerate(grid):
            letter = "{}".format(string.ascii_uppercase[i])
            frameButton = QtWidgets.QFrame()
            frameButton.setFixedHeight(h)
            frameButton.setContentsMargins(0, 0, 0, 0)
            lay = QtWidgets.QVBoxLayout(frameButton)
            button = QtWidgets.QPushButton(letter)
            button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
            button.clicked.connect(lambda x, letter=letter: self.populate_row(letter)) # Every button click returns 'J'
            lay.addWidget(button)
            vlay.addWidget(frameButton)
            self.buttons.append([])
            for j, val in enumerate(row):
                gridButton = QtWidgets.QPushButton()
                gridButton.setFixedSize(h, h)
                self.buttons[i].append(gridButton)
                self.gridLayout.addWidget(gridButton, i, j)
        for ix in range(j + 1):
            label = QtWidgets.QLabel("{}".format(ix + 1))
            label.setAlignment(QtCore.Qt.AlignCenter)
            self.gridLayout.addWidget(label, i + 1, ix)
        vlay.addWidget(QtWidgets.QLabel())
        self.update_data()


    def update_data(self):
        for i, row in enumerate(grid):
            for j, val in enumerate(row):
                btn = self.buttons[i][j]
                if isinstance(val, list):
                    btn.setText(val[0])
                    if val[1] == "GREEN":
                        btn.setStyleSheet(BTN.format('white', 'green'))
                    elif val[1] == "RED":
                        btn.setStyleSheet(BTN.format('white', 'red'))
                else:
                    btn.setStyleSheet(BTN.format('black', 'white'))
                    btn.setText("{}".format(val))


    def center_widget(self):
        self.window().setGeometry(
            QtWidgets.QStyle.alignedRect(
                QtCore.Qt.LeftToRight,
                QtCore.Qt.AlignCenter,
                self.window().size(),
                QtWidgets.QApplication.desktop().availableGeometry())
        )

    def populate_row(self, letter):
        dialog = QtWidgets.QDialog()
        dialog.resize(660, 360)
        textBox = QtWidgets.QPlainTextEdit(dialog)
        Rbtn = QtWidgets.QPushButton('Add To Row')
        Rbtn.clicked.connect(lambda: self.input_to_grid(textBox.toPlainText(), letter))       

        layout = QtWidgets.QVBoxLayout(dialog)
        layout.addWidget(textBox)
        layout.addWidget(Rbtn)
        dialog.exec_()

    def input_to_grid(self, text, letter):
        print(text, letter)
        lst = text.split(' ')
        for i, x in enumerate(lst):
            self.add_to_grid((letter, i + 1), x)
            print((letter, i + 1), x)
        self.print_grid()
        self.update_data()

    def add_to_grid(self, loc, item):
        x, y = self.translate(loc)
        grid[x][y] = item

    def translate(self, loc):
        for k, v in self.letter_count.items():
            if loc[0].upper() == k:
                x = v
        y = int(loc[1]) - 1
        return x, y

    def print_grid(self):
        """ Print the grid prettier than normal.. """
        print('\n'.join(['\t'.join([str(c) for c in r]) for r in grid]))


if __name__ == '__main__':
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

Upvotes: 2

Related Questions