https://pastein.ru/t/_ru

  скопируйте уникальную ссылку для отправки

Загрузка данных


import sys
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit,
    QPushButton, QListWidget, QListWidgetItem, QLabel, QMessageBox,
    QFileDialog, QInputDialog, QDialog, QComboBox, QTextEdit
)
from PyQt5.QtCore import Qt
from docx import Document
from docx.shared import Pt, RGBColor
from datetime import datetime, timedelta

class AdditionalDialog(QDialog):
    def __init__(self, additional_data, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Дополнительно")
        self.additional_data = additional_data
        self.layout = QVBoxLayout()
        
        self.interval_combo = QComboBox()
        self.interval_combo.addItems(["еженедельно", "раз в 2 недели", "раз в 3 недели", "раз в 4 недели"])
        self.layout.addWidget(QLabel("Интервал:"))
        self.layout.addWidget(self.interval_combo)
        
        self.day_of_week_combo = QComboBox()
        self.day_of_week_combo.addItems(["понедельник", "вторник", "среда", "четверг", "пятница", "суббота", "воскресенье"])
        self.layout.addWidget(QLabel("День недели:"))
        self.layout.addWidget(self.day_of_week_combo)

        self.tasks_edit = QTextEdit()
        self.layout.addWidget(QLabel("Задания (каждое с новой строки)"))
        self.layout.addWidget(self.tasks_edit)

        btn_layout = QHBoxLayout()
        btn_ok = QPushButton("Добавить")
        btn_ok.clicked.connect(self.add_tasks)
        btn_layout.addWidget(btn_ok)
        btn_cancel = QPushButton("Отмена")
        btn_cancel.clicked.connect(self.reject)
        btn_layout.addWidget(btn_cancel)
        self.layout.addLayout(btn_layout)
        self.setLayout(self.layout)

    def add_tasks(self):
        interval_text = self.interval_combo.currentText()
        if interval_text == "еженедельно":
            interval = "еженедельно"
        else:
            interval = interval_text.split(" ")[-2]  # Получаем число из "раз в N недели"
        day = self.day_of_week_combo.currentText()
        text = self.tasks_edit.toPlainText().strip()
        lines = [x.strip() for x in text.split("\n") if x.strip()]
        if interval == "еженедельно":
            if "еженедельно" not in self.additional_data:
                self.additional_data["еженедельно"] = {}
            if day not in self.additional_data["еженедельно"]:
                self.additional_data["еженедельно"][day] = []
            self.additional_data["еженедельно"][day].extend(lines)
        else:
            if interval not in self.additional_data:
                self.additional_data[interval] = {}
            if day not in self.additional_data[interval]:
                self.additional_data[interval][day] = []
            self.additional_data[interval][day].extend(lines)
        self.accept()

class EditTasksDialog(QDialog):
    def __init__(self, tasks, title, parent=None):
        super().__init__(parent)
        self.setWindowTitle(title)
        self.tasks = tasks
        self.layout = QVBoxLayout()
        self.text_edit = QTextEdit()
        joined = "\n".join(self.tasks)
        self.text_edit.setPlainText(joined)
        self.layout.addWidget(self.text_edit)
        btn_layout = QHBoxLayout()
        btn_ok = QPushButton("Сохранить")
        btn_ok.clicked.connect(self.save_tasks)
        btn_layout.addWidget(btn_ok)
        btn_cancel = QPushButton("Отмена")
        btn_cancel.clicked.connect(self.reject)
        btn_layout.addWidget(btn_cancel)
        self.layout.addLayout(btn_layout)
        self.setLayout(self.layout)

    def save_tasks(self):
        text = self.text_edit.toPlainText().strip()
        lines = [x.strip() for x in text.split("\n") if x.strip()]
        self.tasks.clear()
        self.tasks.extend(lines)
        self.accept()

class AdditionalViewDialog(QDialog):
    def __init__(self, additional_data, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Просмотр: Дополнительно")
        self.additional_data = additional_data
        self.layout = QVBoxLayout()
        self.list_widget = QListWidget()
        self.layout.addWidget(self.list_widget)
        self.btn_edit = QPushButton("Редактировать выбранное")
        self.btn_edit.clicked.connect(self.edit_selected)
        self.layout.addWidget(self.btn_edit)
        self.setLayout(self.layout)
        self.populate()

    def populate(self):
        self.list_widget.clear()
        for interval, days_dict in self.additional_data.items():
            for day, tasks in days_dict.items():
                if interval == "еженедельно":
                    interval_display = "еженедельно"
                else:
                    interval_display = f"раз в {interval} недели"
                item_text = f"{interval_display}: {day} ({len(tasks)} заданий)"
                self.list_widget.addItem(item_text)

    def edit_selected(self):
        item = self.list_widget.currentItem()
        if not item:
            return
        text = item.text()
        if text.startswith("еженедельно: "):
            interval = "еженедельно"
            rest = text[len("еженедельно: "):]
        else:
            parts = text.split(": ", 1)
            interval_part = parts[0]
            interval = interval_part.split(" ")[-2]
            rest = parts[1]
        day_part = rest.split(" (")[0]
        if interval not in self.additional_data:
            return
        if day_part not in self.additional_data[interval]:
            return
        tasks = self.additional_data[interval][day_part]
        dlg = EditTasksDialog(tasks, f"{interval} недели - {day_part}", self)
        if dlg.exec_():
            self.populate()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Рутина")
        self.resize(800, 500)

        self.categories = []
        self.setup_default_categories()
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.main_layout = QHBoxLayout(self.central_widget)

        self.category_list = QListWidget()
        self.category_list.itemSelectionChanged.connect(self.on_category_selection_changed)
        self.main_layout.addWidget(self.category_list)

        self.task_list = QListWidget()
        self.main_layout.addWidget(self.task_list)

        self.right_panel = QVBoxLayout()

        # Регулярные кнопки
        self.regular_widget = QWidget()
        self.regular_layout = QVBoxLayout()
        self.regular_widget.setLayout(self.regular_layout)

        self.btn_add_category = QPushButton("Добавить категорию")
        self.btn_add_category.clicked.connect(self.add_category)
        self.btn_remove_category = QPushButton("Удалить категорию")
        self.btn_remove_category.clicked.connect(self.remove_category)
        self.btn_rename_category = QPushButton("Переименовать категорию")
        self.btn_rename_category.clicked.connect(self.rename_category)
        self.btn_add_task = QPushButton("Добавить задания")
        self.btn_add_task.clicked.connect(self.add_task)
        self.btn_edit_task = QPushButton("Редактировать задания")
        self.btn_edit_task.clicked.connect(self.edit_tasks)
        self.btn_export = QPushButton("Экспорт в Word")
        self.btn_export.clicked.connect(self.export_to_word)

        self.regular_layout.addWidget(self.btn_add_category)
        self.regular_layout.addWidget(self.btn_remove_category)
        self.regular_layout.addWidget(self.btn_rename_category)
        self.regular_layout.addWidget(self.btn_add_task)
        self.regular_layout.addWidget(self.btn_edit_task)
        self.regular_layout.addWidget(self.btn_export)
        self.regular_layout.addStretch()

        # Кнопки вверх и вниз с символами
        self.move_buttons_layout = QHBoxLayout()
        self.btn_up = QPushButton("↑")
        self.btn_up.clicked.connect(self.move_up)
        self.btn_down = QPushButton("↓")
        self.btn_down.clicked.connect(self.move_down)
        self.move_buttons_layout.addWidget(self.btn_up)
        self.move_buttons_layout.addWidget(self.btn_down)
        self.regular_layout.addLayout(self.move_buttons_layout)

        # Дополнительные кнопки
        self.additional_widget = QWidget()
        self.additional_layout = QVBoxLayout()
        self.additional_widget.setLayout(self.additional_layout)

        self.btn_edit_additional = QPushButton("Редактировать")
        self.btn_edit_additional.clicked.connect(self.open_additional_dialog)
        self.btn_preview_additional = QPushButton("Предпросмотр")
        self.btn_preview_additional.clicked.connect(self.open_additional_view)
        self.btn_export_additional = QPushButton("Экспорт в Word")
        self.btn_export_additional.clicked.connect(self.export_to_word)

        self.additional_layout.addWidget(self.btn_edit_additional)
        self.additional_layout.addWidget(self.btn_preview_additional)
        self.additional_layout.addWidget(self.btn_export_additional)
        self.additional_layout.addStretch()

        # Добавление виджетов кнопок в правую панель
        self.right_panel.addWidget(self.regular_widget)
        self.right_panel.addWidget(self.additional_widget)
        self.right_panel.addStretch()
        rwidget = QWidget()
        rwidget.setLayout(self.right_panel)
        self.main_layout.addWidget(rwidget)

        self.refresh_categories()
        self.update_button_visibility()

    def setup_default_categories(self):
        self.categories.append({"name": "После сна", "tasks": [], "additional": None})
        self.categories.append({"name": "Вечером", "tasks": [], "additional": None})
        self.categories.append({"name": "Дополнительно", "tasks": [], "additional": {}})
        self.categories.append({"name": "Перед сном", "tasks": [], "additional": None})

    def refresh_categories(self):
        self.category_list.clear()
        for cat in self.categories:
            self.category_list.addItem(cat["name"])
        self.show_tasks()

    def show_tasks(self):
        self.task_list.clear()
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories):
            return
        cat = self.categories[idx]
        if cat["additional"] is None:
            for t in cat["tasks"]:
                self.task_list.addItem(t)

    def add_category(self):
        text, ok = QInputDialog.getText(self, "Добавить категорию", "Название категории:")
        if ok and text.strip():
            capitalized = text.strip().capitalize()
            self.categories.append({"name": capitalized, "tasks": [], "additional": None})
            self.refresh_categories()
            # Автоматически выделить последнюю добавленную категорию
            self.category_list.setCurrentRow(len(self.categories) - 1)

    def remove_category(self):
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories):
            return
        reply = QMessageBox.question(self, "Удалить категорию", f"Удалить '{self.categories[idx]['name']}'?",
                                     QMessageBox.Yes | QMessageBox.No)
        if reply == QMessageBox.Yes:
            del self.categories[idx]
            self.refresh_categories()

    def rename_category(self):
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories):
            return
        old_name = self.categories[idx]["name"]
        text, ok = QInputDialog.getText(self, "Переименовать категорию", "Новое название:", text=old_name)
        if ok and text.strip():
            capitalized = text.strip().capitalize()
            self.categories[idx]["name"] = capitalized
            self.refresh_categories()
            self.category_list.setCurrentRow(idx)  # Сохранить выбор

    def move_up(self):
        idx = self.category_list.currentRow()
        if idx <= 0 or idx >= len(self.categories):
            return
        self.categories[idx], self.categories[idx-1] = self.categories[idx-1], self.categories[idx]
        self.refresh_categories()
        self.category_list.setCurrentRow(idx-1)

    def move_down(self):
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories)-1:
            return
        self.categories[idx], self.categories[idx+1] = self.categories[idx+1], self.categories[idx]
        self.refresh_categories()
        self.category_list.setCurrentRow(idx+1)

    def add_task(self):
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories):
            return
        cat = self.categories[idx]
        if cat["additional"] is not None:
            QMessageBox.information(self, "Внимание", "В этой категории используйте кнопку 'Дополнительно'.")
            return
        text, ok = QInputDialog.getMultiLineText(self, "Добавить задания", "Каждое задание с новой строки:")
        if ok and text.strip():
            lines = [x.strip() for x in text.split("\n") if x.strip()]
            cat["tasks"].extend(lines)
            self.refresh_categories()

    def edit_tasks(self):
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories):
            return
        cat = self.categories[idx]
        if cat["additional"] is not None:
            QMessageBox.information(self, "Внимание", "В этой категории используйте кнопку 'Просмотр дополнительно'.")
            return
        dlg = EditTasksDialog(cat["tasks"], cat["name"], self)
        if dlg.exec_():
            self.refresh_categories()

    def open_additional_dialog(self):
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories):
            return
        cat = self.categories[idx]
        if cat["name"] != "Дополнительно":
            QMessageBox.information(self, "Нет поддержки", "Эта категория не поддерживает 'Дополнительно'.")
            return
        if cat["additional"] is None:
            cat["additional"] = {}
        dlg = AdditionalDialog(cat["additional"], self)
        if dlg.exec_():
            self.refresh_categories()
            # Автоматически выделить "Дополнительно" после добавления
            for i, category in enumerate(self.categories):
                if category["name"] == "Дополнительно":
                    self.category_list.setCurrentRow(i)
                    break

    def open_additional_view(self):
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories):
            return
        cat = self.categories[idx]
        if cat["additional"] is None:
            QMessageBox.information(self, "Нет поддержки", "Эта категория не поддерживает 'Дополнительно'.")
            return
        dlg = AdditionalViewDialog(cat["additional"], self)
        dlg.exec_()

    def export_to_word(self):
        start_str, ok1 = QInputDialog.getText(self, "Начальная дата", "Введите дату в формате ДД.М.ГГГГ:")
        if not ok1 or not start_str.strip():
            return
        end_str, ok2 = QInputDialog.getText(self, "Конечная дата", "Введите дату в формате ДД.М.ГГГГ:")
        if not ok2 or not end_str.strip():
            return
        try:
            start_date = datetime.strptime(start_str, "%d.%m.%Y")
            end_date = datetime.strptime(end_str, "%d.%m.%Y")
        except:
            QMessageBox.warning(self, "Ошибка", "Неверный формат даты.")
            return
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getSaveFileName(self, "Сохранить файл", "", "Word Documents (*.docx)", options=options)
        if not file_path:
            return
        if not file_path.lower().endswith(".docx"):
            file_path += ".docx"
        try:
            document = Document()
            style = document.styles["Normal"]
            style.font.name = "Calibri"
            style.font.size = Pt(12)
            style.font.color.rgb = RGBColor(0, 0, 0)
            current_date = start_date
            day_name_map = {
                "Monday": "понедельник",
                "Tuesday": "вторник",
                "Wednesday": "среда",
                "Thursday": "четверг",
                "Friday": "пятница",
                "Saturday": "суббота",
                "Sunday": "воскресенье"
            }
            month_name_map = {
                "January": "января",
                "February": "февраля",
                "March": "марта",
                "April": "апреля",
                "May": "мая",
                "June": "июня",
                "July": "июля",
                "August": "августа",
                "September": "сентября",
                "October": "октября",
                "November": "ноября",
                "December": "декабря"
            }
            while current_date <= end_date:
                day_eng = current_date.strftime("%A")
                day_rus = day_name_map.get(day_eng, "")
                month_eng = current_date.strftime("%B")
                month_rus = month_name_map.get(month_eng, "")
                date_header = f"{current_date.day} {month_rus} ({day_rus})"
                p = document.add_paragraph()
                run = p.add_run(date_header)
                run.bold = True
                document.add_paragraph()
                week_number = current_date.isocalendar()[1]
                for cat in self.categories:
                    if cat["additional"] is not None:
                        all_tasks = []
                        # Еженедельные задачи
                        weekly_tasks = cat["additional"].get("еженедельно", {}).get(day_rus, [])
                        all_tasks.extend(weekly_tasks)
                        # Задачи с интервалами
                        for interval_str, days_dict in cat["additional"].items():
                            if interval_str == "еженедельно":
                                continue
                            try:
                                interval = int(interval_str)
                                if interval > 0 and week_number % interval == 0:
                                    special_tasks = days_dict.get(day_rus, [])
                                    all_tasks.extend(special_tasks)
                            except:
                                continue
                        if all_tasks:
                            cat_par = document.add_paragraph()
                            cat_run = cat_par.add_run(cat["name"].lower())
                            cat_run.bold = True
                            for t in all_tasks:
                                document.add_paragraph(t)
                            document.add_paragraph()
                    else:
                        if cat["tasks"]:
                            cat_par = document.add_paragraph()
                            cat_run = cat_par.add_run(cat["name"].lower())
                            cat_run.bold = True
                            for t in cat["tasks"]:
                                document.add_paragraph(t)
                            document.add_paragraph()
                current_date += timedelta(days=1)
            document.save(file_path)
            QMessageBox.information(self, "Готово", f"Файл сохранён: {file_path}")
        except Exception as e:
            QMessageBox.warning(self, "Ошибка", str(e))

    def on_category_selection_changed(self):
        self.show_tasks()
        self.update_button_visibility()

    def update_button_visibility(self):
        idx = self.category_list.currentRow()
        if idx < 0 or idx >= len(self.categories):
            self.regular_widget.hide()
            self.additional_widget.hide()
            return
        cat = self.categories[idx]
        if cat["name"] != "Дополнительно":
            self.regular_widget.show()
            self.additional_widget.hide()
        else:
            self.regular_widget.hide()
            self.additional_widget.show()

def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()