SUAI-TaskPlanner-Contest / TaskPlanner

Client application for working with todos and syncing with CalDAV servers
MIT License
3 stars 2 forks source link

Создать окно предупреждений #66

Closed astronik00 closed 1 year ago

astronik00 commented 1 year ago

Design

Требования: программист

Краткое описание

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

Функциональные требования или иные документы

Пример: Image

На окне должны быть четыре элемента: название, развернутый текст, кнопка 1, кнопка 2. Ситуации, которые нужно рассмотреть:

Более конкретный и подходящий под ситуацию текст реализовать самостоятельно. Расширять список, если будут обнаружены еще ситуации.

Обобщенное решение

Выходной результат

Файл окна QML. В MR загрузить скриншот работы.

pavelparfishov commented 1 year ago

@astronik00

Получилось такое окно:

Image

Вывел отдельно переменные для изменения текстов:

Image

astronik00 commented 1 year ago

@pavelparfishov

А как передать текст содержимого ошибки из другого окна? Или например, как передать код ошибки на окно предупреждения, а в зависимости от него выбрать нужные строки (из массива ошибок, например, который бы хранился в окне)?

astronik00 commented 1 year ago

@pavelparfishov В принципе, можно и без передачи ошибки между окнами, но тогда придется создавать подобное окно на каждое возможное предупреждение.

pavelparfishov commented 1 year ago

Для передачи текста из одного QML-окна в другое можно использовать сигналы и слоты.

Пример: У нас есть QML-окно MainWindow.qml с кнопкой, при нажатии на которую открывается второе окно SecondWindow.qml, в котором мы хотим отобразить текст, переданный из MainWindow.qml.

В MainWindow.qml нужно объявить сигнал и обработчик нажатия на кнопку:

import QtQuick 2.0

Item {
    signal textToPass(string text)

    Button {
        text: "Open second window"
        onClicked: {
            textToPass("Hello from Main Window!")
        }
    }
}

В SecondWindow.qml нужно объявить слот для принятия сигнала и отобразить переданный текст:

import QtQuick 2.0

Item {
    id: secondWindow

    property string textToDisplay: ""

    Connections {
        target: main
        onTextToPass: {
            secondWindow.textToDisplay = text;
            secondWindow.visible = true;
        }
    }

    Text {
        text: secondWindow.textToDisplay
    }
}

Здесь мы объявляем свойство textToDisplay, которое будет использоваться для отображения текста в окне. В блоке Connections мы подключаем сигнал textToPass из MainWindow.qml к слоту onTextToPass и передаем текст в свойство textToDisplay. Кроме того, мы делаем окно видимым, чтобы отобразить переданный текст.

Обратите внимание, что мы используем идентификатор main в Connections, который должен быть установлен в корневом элементе ApplicationWindow в файле main.qml:

import QtQuick 2.0

ApplicationWindow {
    id: main
    width: 640
    height: 480
    visible: true

    // ...

    SecondWindow {
        visible: false
    }
}

Здесь мы добавляем в ApplicationWindow компонент SecondWindow, который мы будем использовать для отображения переданного текста. Обратите внимание, что мы устанавливаем свойство visible в false, чтобы окно не отображалось по умолчанию.

pavelparfishov commented 1 year ago

Думаешь, стоит такую логику в разметке интерфейса реализовывать? Можно конечно if-ами попытаться это сделать, но тогда придется постоянно в qml-файл лезть, чтобы добавить информацию по новой ошибке. Мне кажется, что лучше сделать массивы в .py файле или .json или .csv, и при обработке ошибки в .py-файле просто передавать нужные тексты в окно предупреждений.

Пример передачи названия кнопки из Python в QML:

from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
import sys

class ButtonTextProvider(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._button_text = ""

    @pyqtProperty(str, notify=buttonTextChanged)
    def buttonText(self):
        return self._button_text

    @buttonText.setter
    def buttonText(self, text):
        self._button_text = text
        self.buttonTextChanged.emit()

    buttonTextChanged = pyqtSignal()

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    # создаем экземпляр класса ButtonTextProvider и передаем его в QML
    button_text_provider = ButtonTextProvider()
    engine.rootContext().setContextProperty("buttonTextProvider", button_text_provider)

    # получаем текст кнопки из переменной Python
    button_text = "Нажми меня"
    # устанавливаем текст кнопки в свойство buttonText объекта ButtonTextProvider
    button_text_provider.buttonText = button_text

    # запускаем приложение
    engine.load("main.qml")
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())
astronik00 commented 1 year ago

Думаешь, стоит такую логику в разметке интерфейса реализовывать?

А это на какое из двух сообщений? Не понимаю, о какой конкретно

astronik00 commented 1 year ago

Я так-то думаю, что можно создать один класс ошибки, у которой было бы название, текст, а также значения кнопок (последнее не очень красиво звучит для класса, а может и норм), и просто передавать в новое окно ошибку, а дальше:

button_ok_text = MyCustomError.button_ok_text
button_close_text = MyCustomError.button_close_text
pavelparfishov commented 1 year ago

@astronik00 Привет) Вот так получилось сделать:

Image

Image

В QML он вот так подтягивается:

    Text {
        text: myText.text
        anchors.centerIn: parent
    }
astronik00 commented 1 year ago

Мне сложно понять логику, не начав разбирать документацию Qt и QML. Так сделать можно или нельзя?

class CustomException(Exception):
    def __init__(self, title: str, description: str, ok: str, cancel: str) -> None:
        self.title = title
        self.description = description
        self.ok = ok
        self.cancel = cancel

где-то выкинули ошибку:

if a == 1:
    raise CustomException("a == 1", "А оказалось равно 1, это ужасно", "Да", "Нет")

и где-то в файле:

    try:
        pass
    except CustomException as e:
        # вызов окна предупреждения с передачей e.title, e.description, e.ok, e.cancel 
pavelparfishov commented 1 year ago

@astronik00 Как получилось сделать:

Image

Image

Image

Тексты для элементов хранятся в словаре. Подтягиваются по ключу.

Image

Используется класс, который наследуется от QObject, чтобы с QML взаимодействовать. Одновременно от Exception и QObject он наследоваться не может :( . Критично, что он не от Exception наследуется? Или это был просто возможный вариант реализации?

astronik00 commented 1 year ago

@pavelparfishov К сожалению, это обязательно, чтобы можно было выбросить питоновский Exception и использовать его в блоке try-catch.

pavelparfishov commented 1 year ago

@astronik00 Попробовал по-другому (создал еще один класс).

Класс для Exception'ов:

class CustomException(Exception):
    def __init__(self, error_info: Tuple[str, str, str, str]) -> None:
        super().__init__(error_info[1])
        self.error_info = ErrorInfo(*error_info)

Так норм?

Image

Image

pavelparfishov commented 1 year ago

Image

astronik00 commented 1 year ago

@pavelparfishov а класс ErrorInfo откуда берется?

astronik00 commented 1 year ago

а ты можешь это выложить в свою ветку? я примерно поняла, но все еще не очень этот самый ErrorInfo - это как Model, которую ты создал на View ошибки, который подтягивается по названию error_info?

astronik00 commented 1 year ago

если все работает, то думаю, что такой вариант пойдет

pavelparfishov commented 1 year ago

@astronik00 Класс ErrorInfo я создал. Наследуется от QObject, чтобы с QML взаимодействовать. Выложил в ветку 3 файла.

astronik00 commented 1 year ago

@pavelparfishov

Я посмотрела. Тогда будем создавать таким образом ошибки. Само окно выглядит симпатично.

astronik00 commented 1 year ago

@pavelparfishov

Вот еще вспомнила.

self._error_name = _error_name
self._error_text = _error_text

Я думаю error_name уже излишне, стоит просто оставить name и text