Search code examples
python-3.xuser-interfacelogicpyside

How to truly separate logic and UI using PySide


I'm trying to separate logic and UI. In the code below I have UI and logic classes. The goal is to fill the list widget with data - a list of found json files. At first start of the UI it gets data from Data() and fills the list with it. The button which is connected to Data()create() method must create the new file and fill the list with new data. The only way I found to do this is to pass the main window to Data and do refresh which I think is against this model of separating UI and logic. The logic part shouldn't know anything about the UI.

from PySide2 import QtGui
from PySide2 import QtWidgets
import os
import json

HOME = os.path.expanduser("~")

class View(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        resolution = QtGui.QGuiApplication.primaryScreen().availableGeometry()
        self.setMinimumSize(resolution.width() * 0.25, resolution.height() * 0.3)
        self._central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self._central_widget)


        self._list_widget_layout = QtWidgets.QVBoxLayout()
        self._buttons_layout = QtWidgets.QHBoxLayout()
        self._list_widget = QtWidgets.QListWidget()
        self._button = QtWidgets.QPushButton("Button")
        self._central_widget.setLayout(self._list_widget_layout)

        self._list_widget_layout.addWidget(self._list_widget)
        self._list_widget_layout.addLayout(self._buttons_layout)
        self._buttons_layout.addWidget(self._button)

        self._add_info = Data()
        # Connect
        self._button.clicked.connect(lambda: self._add_info.create(self))

        self.fill_list()

    def fill_list(self):
        self._list_widget.clear()
        for i in self._add_info.get_folder_content_by_extension(path=HOME, ext="json"):
            item = QtWidgets.QListWidgetItem()
            item.setText(i)
            self._list_widget.addItem(item)


class Data(object):

    def create(self, main_window):
        data_dict = {"A": "1", "B": "2", "C": "3" }
        self.save_json(path=os.path.join(HOME, "new_file.json"), save_dict=data_dict)
        main_window.fill_list()


    @classmethod
    def get_folder_content_by_extension(cls, path=None, ext=None):
        folder_content = list()

        for f in os.listdir(path):
            if f.endswith(".{}".format(ext)):
                folder_content.append(os.path.join(path, f))

        return folder_content

    @classmethod
    def save_json(cls, path, save_dict):
        with open(path, "w") as json_file:
            json.dump(save_dict, json_file, indent=4)

if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    w = View()
    w.show()
    app.exec_()

Solution

  • I noticed that actually I can connect the same button to fill_list method just after it connects to self._add_info.create. Separate UI and logic is achieved. I don't know if this is right way.

    from PySide2 import QtGui
    from PySide2 import QtWidgets
    import os
    import json
    
    HOME = os.path.expanduser("~")
    
    class View(QtWidgets.QMainWindow):
    
        def __init__(self):
            super().__init__()
    
            resolution = QtGui.QGuiApplication.primaryScreen().availableGeometry()
            self.setMinimumSize(resolution.width() * 0.25, resolution.height() * 0.3)
            self._central_widget = QtWidgets.QWidget()
            self.setCentralWidget(self._central_widget)
    
    
            self._list_widget_layout = QtWidgets.QVBoxLayout()
            self._buttons_layout = QtWidgets.QHBoxLayout()
            self._list_widget = QtWidgets.QListWidget()
            self._button = QtWidgets.QPushButton("Button")
            self._central_widget.setLayout(self._list_widget_layout)
    
            self._list_widget_layout.addWidget(self._list_widget)
            self._list_widget_layout.addLayout(self._buttons_layout)
            self._buttons_layout.addWidget(self._button)
    
            self._add_info = Data()
            # Connect
            self._button.clicked.connect(lambda: self._add_info.create())
            self._button.clicked.connect(self.fill_list)
    
    
            self.fill_list()
    
        def fill_list(self):
            self._list_widget.clear()
            for i in self._add_info.get_folder_content_by_extension(path=HOME, ext="json"):
                item = QtWidgets.QListWidgetItem()
                item.setText(i)
                self._list_widget.addItem(item)
    
    
    class Data(object):
    
        def create(self):
            data_dict = {"A": "1", "B": "2", "C": "3" }
            self.save_json(path=os.path.join(HOME, "new_file.json"), save_dict=data_dict)
    
    
        @classmethod
        def get_folder_content_by_extension(cls, path=None, ext=None):
            folder_content = list()
    
            for f in os.listdir(path):
                if f.endswith(".{}".format(ext)):
                    folder_content.append(os.path.join(path, f))
    
            return folder_content
    
        @classmethod
        def save_json(cls, path, save_dict):
            with open(path, "w") as json_file:
                json.dump(save_dict, json_file, indent=4)
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication([])
        w = View()
        w.show()
        app.exec_()