Как написать класс, с подтверждением окончания обращения к нему и возможностью отмены последнего действия выполненного в нем?

Есть класс с двумя методами:

  • method1 прибавляет по единице (инкремент);
  • method2 принимает строку и добавляет её в список.

Что нужно:

  1. После окончания работы с методом (не после каждого вызова, а после того, как он стал не нужен) надо это обозначить, вызвав escape_method().
    Если забыли это сделать, то:

    • второй метод будет недоступен для обращения
    • по окончании работы с классом (см. далее) не появится меню.
  2. Также надо обозначить окончание работы с классом (вызов метода escape_class()).
    Этот метод вернёт меню 'да' \ 'нет'.

  • Если 'да' - изменения сохранились, и мы можем получить переменные.
  • Если 'нет' - не сохранились.

Вызовется, только если отработавшие method1 и method2 были закрыты. После 'закрытия' класса можно получить my_list и counter через вызов get_data().

  1. Единовременно в работе может быть только один метод (method1() или method2()), и надо понимать какой именно.
    Этим занимается переменная, в которую записывается название текущего метода, и эту переменную можно получить. Она сохраняет это состояние между вызовами метода, и обнуляется после закрытия (escape_method) текущего метода.

  2. Нужна возможность отмены последнего действия для method1 и method2.

Методы между собой не связаны; окончание работы одного не обязывает запускать другой.

Пункты 1 - 3 примерно сделал, но мне не нравится как (в частности, флажки это не очень надёжно). Как это переделать? Наверное, нужны декораторы или внешний управляющий класс/классы.

Кроме того, класс должен возвращать переменные my_list и counter только методом get_data и только после своего закрытия (а значит и закрытия методов). Как это реализовать?

Пункт 4 получается очень громоздко, даже не хочу выкладывать. Как сделать отмену последней операции?

class DataProcessing:
    def __init__(self):
        self.my_list = []                    # method1
        self.counter = 0                     # method2
        self.current_method = 0              # текущий метод
        self.are_methods_closed = True       # флажок закрытия метода
        self.is_class_closed = True          # флажок закрытия класса

    def method1(self):
        if self.are_methods_closed:
            self.are_methods_closed = False           # начало сессии этого метода
            self.current_method = 1
            self.counter += 1
        elif self.are_methods_closed == False and self.current_method == 1:   # продолжение сессии этого метода
            self.counter += 1

    def method2(self, value):
        if self.are_methods_closed:
            self.are_methods_closed = False
            self.current_method = 2
            self.my_list.append(value)
        elif self.are_methods_closed == False and self.current_method == 2:
            self.my_list.append(value)

    def escape_method(self):
        self.are_methods_closed = True
        self.current_method = 0

    def escape_class(self):
        if self.are_methods_closed:
            text = input("Сохранить изменения? \n Введите Да либо Нет: ")
            if text == 'Да':
                print('Изменения сохранены')
                self.is_class_closed = True
            elif text == 'Нет':
                print('Изменения не сохранены')
                self.is_class_closed = True
            else:
                pass

    def get_data(self, n):
        if n == 1:
            return(self.counter)
        elif n == 2:
            return (self.my_list)

Ответы (1 шт):

Автор решения: S. Nick

Я попробовал реализовать то, что вы написали.
Не факт, что я правильно понял то, что вы хотите сделать.
Попробуйте запустить мой пример и потихоньку нажимайте доступные кнопки и внимательно смотрите что происходит на экране.

import sys
import random
from PyQt5.Qt import *


class DataProcessing(QMainWindow):
    def __init__(self):
        super().__init__()
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        
        self.my_list = []                                            # method2
        self.counter = 0                                             # method1

        self.label_1 = QLabel("Method 1", alignment = Qt.AlignCenter)
        self.label_2 = QLabel("Method 2", alignment = Qt.AlignCenter)

        self.textBrowser_1 = QTextBrowser()
        self.textBrowser_2 = QTextBrowser()
        
        self.button_1 = QPushButton("Go method1")
        self.button_1.clicked.connect(self.method1)
        self.button_1.setCheckable(True)

        self.button_2 = QPushButton("Go method2")
        self.button_2.setCheckable(True)
        self.button_2.clicked.connect(
            lambda ch, value=None: self.method2(ch, value))
        
        self.layout = QGridLayout(self.centralWidget)
        self.layout.addWidget(self.label_1, 0, 0)
        self.layout.addWidget(self.label_2, 0, 1)
        self.layout.addWidget(self.textBrowser_1, 1, 0)
        self.layout.addWidget(self.textBrowser_2, 1, 1)
        self.layout.addWidget(self.button_1, 2, 0)
        self.layout.addWidget(self.button_2, 2, 1)
        
        self.timer = QTimer()
        self.timer.timeout.connect(self._update)
        self.timer.setInterval(1500)
        self.timer.start()
        
    def _update(self):        
        if self.button_1.isChecked():
            self.method1(True)
        elif self.button_2.isChecked():
            value = random.randint(0, 100)
            self.method2(True, value)        
        
    def method1(self, state):
        """ method1 прибавляет по единице (инкремент) """

        if state:
            self.button_2.setEnabled(False)
            self.button_1.setText("Остановить method1")
            self.counter += 1
            self.textBrowser_1.setHtml(
                f'Работает  method1.<br> counter = {self.counter}<br>')
        else:
            self.button_1.setText("Go method1") 
            self.button_2.setEnabled(True)
            self.textBrowser_1.moveCursor(QTextCursor.End)            
            self.textBrowser_1.insertHtml(
                '<b style="font-size: 22px; color: red">method1 отклучен</b> <br><br>')
        
            self.escape_method() 

    def method2(self, state, value):
        """ method2 принимает строку и добавляет её в список """
        
        if state:
            self.button_1.setEnabled(False)
            if value:
                self.button_2.setText("Остановить method2")
                self.my_list.append(value)
                self.textBrowser_2.setHtml(
                    f'Работает  method2.<br> item = {value}; '
                    f'записей {len(self.my_list)}<br>')            
        else:
            self.button_1.setEnabled(True)
            self.button_2.setText("Go method2")            
            self.textBrowser_2.moveCursor(QTextCursor.End)            
            self.textBrowser_2.insertHtml(
                '<b style="font-size: 22px; color: red">method2 отклучен</b> <br><br>')
                    
            self.escape_method()

    def escape_method(self):
        if not self.button_1.isChecked() and not self.button_2.isChecked(): 
            self.escape_class()
        
    def escape_class(self):
        msg = QMessageBox.question(
            self, 
            "Сообщение вопроса",
            "Сохранить изменения?      ",
            QMessageBox.Yes | QMessageBox.No)
            
        if msg == QMessageBox.Yes:
            print('Изменения сохранены')
            self.get_data()
        else:
            print('Изменения не сохранены')
            self.method4()
            
    def method4(self):
        msg = QMessageBox(self)
        msg.setWindowIcon(QIcon("icono.png"))
        msg.setWindowTitle("Пользовательское сообщение")
        msg.setIconPixmap(QPixmap("Qt.png").scaled(100, 100, Qt.KeepAspectRatio))
        msg.setText("Это окно сообщения <b>ПОЛЬЗОВАТЕЛЬСКОЕ</b>    ")
        msg.setInformativeText(
            """Есть возможность отмены последнего действия для method1 или method2. 
Выберите какое действие вы хотите отменить или откажитесь.
            """)
            
        buttonMethod1 = msg.addButton("method1", QMessageBox.YesRole)           
        buttonMethod2 = msg.addButton("method2", QMessageBox.AcceptRole)
        buttonCancel = msg.addButton("Отменить", QMessageBox.RejectRole)
        msg.exec_()
        
        if msg.clickedButton() == buttonMethod1:
            self.counter -= 1
            self.textBrowser_1.insertHtml(
                f'Отменено последнее действие method1.<br> '
                f'counter = {self.counter}<br><br>')        
        elif msg.clickedButton() == buttonMethod2:
            if not self.my_list:
                # my_list - пустой
                return
            self.my_list.pop()
            self.textBrowser_2.insertHtml(
                f'Отменено последнее действие method2.<br> '
                f'Всего элементов {len(self.my_list)}, '
                f'последний item = {self.my_list[-1]}<br><br>')  
        elif msg.clickedButton() == buttonCancel:
            pass
    
    def get_data(self):                          #, n):           n <---- ?
#        if n == 1: return(self.counter)
#        elif n == 2: return (self.my_list)

        self.textBrowser_1.insertHtml(
            f'''<b style="font-size: 18px; color: green">
             == The End ==<br><br>
             counter={self.counter}
            </b> <br><br>''')

        self.textBrowser_2.insertHtml(
            f'''<b style="font-size: 18px; color: green">
             == The End ==<br>
            </b> <br>''')
        for item in self.my_list:
            self.textBrowser_2.insertHtml(
                f'''<b style="font-size: 18px; color: green">
                 item={item}
                </b> <br>''')        


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setFont(QFont("Times", 12, QFont.Bold))
    window = DataProcessing()
    window.resize(600, 400)
    window.show()
    sys.exit(app.exec())

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка