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()