<===

ProNotes

2025-10-02 08:44:15
$ cat notes.py 
import sys
import sqlite3
from datetime import datetime
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                            QLineEdit, QTextEdit, QPushButton, QTableWidget, QTableWidgetItem,
                            QMessageBox, QHeaderView, QInputDialog)
from PyQt5.QtCore import Qt

class NotebookApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Записная книжка")
        self.setGeometry(100, 100, 800, 600)

        # Инициализация базы данных
        self.conn = sqlite3.connect("notebook.db")
        self.create_database()

        # Основной виджет и компоновка
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QVBoxLayout(self.central_widget)

        # Поле для ввода заметки
        self.note_input = QTextEdit()
        self.note_input.setPlaceholderText("Введите заметку (до 255 символов)")
        self.note_input.setFixedHeight(100)
        self.layout.addWidget(self.note_input)

        # Кнопки для добавления и очистки
        self.button_layout = QHBoxLayout()
        self.add_button = QPushButton("Добавить заметку")
        self.add_button.clicked.connect(self.add_note)
        self.clear_button = QPushButton("Очистить")
        self.clear_button.clicked.connect(self.clear_input)
        self.button_layout.addWidget(self.add_button)
        self.button_layout.addWidget(self.clear_button)
        self.layout.addLayout(self.button_layout)

        # Поле для поиска
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("Введите запрос для поиска...")
        self.search_input.textChanged.connect(self.search_notes)
        self.layout.addWidget(self.search_input)

        # Таблица для отображения заметок
        self.table = QTableWidget()
        self.table.setColumnCount(3)
        self.table.setHorizontalHeaderLabels(["ID", "Дата", "Заметка"])
        self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
        self.table.setColumnWidth(0, 50)
        self.table.setColumnWidth(1, 150)
        self.table.setSelectionBehavior(QTableWidget.SelectRows)
        self.table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table.cellDoubleClicked.connect(self.edit_note)
        self.layout.addWidget(self.table)

        # Кнопки для редактирования и удаления
        self.action_layout = QHBoxLayout()
        self.edit_button = QPushButton("Редактировать")
        self.edit_button.clicked.connect(self.edit_selected_note)
        self.delete_button = QPushButton("Удалить")
        self.delete_button.clicked.connect(self.delete_note)
        self.action_layout.addWidget(self.edit_button)
        self.action_layout.addWidget(self.delete_button)
        self.layout.addLayout(self.action_layout)

        # Загрузка заметок при запуске
        self.load_notes()

    def create_database(self):
        cursor = self.conn.cursor()
        # Создание таблицы notes
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS notes (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                date TEXT NOT NULL,
                note TEXT NOT NULL
            )
        """)
        # Создание виртуальной таблицы для FTS5
        cursor.execute("""
            CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(
                note,
                content='notes',
                content_rowid='id'
            )
        """)
        # Создание триггеров для синхронизации FTS5 с таблицей notes
        cursor.execute("""
            CREATE TRIGGER IF NOT EXISTS notes_insert AFTER INSERT ON notes
            BEGIN
                INSERT INTO notes_fts(rowid, note) VALUES (new.id, new.note);
            END
        """)
        cursor.execute("""
            CREATE TRIGGER IF NOT EXISTS notes_update AFTER UPDATE ON notes
            BEGIN
                INSERT INTO notes_fts(rowid, note) VALUES (new.id, new.note);
            END
        """)
        cursor.execute("""
            CREATE TRIGGER IF NOT EXISTS notes_delete AFTER DELETE ON notes
            BEGIN
                INSERT INTO notes_fts(notes_fts, rowid, note) VALUES ('delete', old.id, old.note);
            END
        """)
        self.conn.commit()

    def add_note(self):
        note = self.note_input.toPlainText().strip()
        if not note:
            QMessageBox.warning(self, "Ошибка", "Заметка не может быть пустой!")
            return
        if len(note) > 255:
            QMessageBox.warning(self, "Ошибка", "Заметка не может превышать 255 символов!")
            return

        date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        cursor = self.conn.cursor()
        cursor.execute("INSERT INTO notes (date, note) VALUES (?, ?)", (date, note))
        self.conn.commit()
        self.note_input.clear()
        self.load_notes()

    def clear_input(self):
        self.note_input.clear()
        self.search_input.clear()

    def load_notes(self):
        cursor = self.conn.cursor()
        cursor.execute("SELECT id, date, note FROM notes ORDER BY date DESC")
        rows = cursor.fetchall()

        self.table.setRowCount(len(rows))
        for row_idx, (id_, date, note) in enumerate(rows):
            self.table.setItem(row_idx, 0, QTableWidgetItem(str(id_)))
            self.table.setItem(row_idx, 1, QTableWidgetItem(date))
            self.table.setItem(row_idx, 2, QTableWidgetItem(note))

    def search_notes(self):
        query = self.search_input.text().strip()
        cursor = self.conn.cursor()
        if query:
            cursor.execute("""
                SELECT n.id, n.date, n.note
                FROM notes n
                JOIN notes_fts f ON n.id = f.rowid
                WHERE notes_fts MATCH ?
                ORDER BY n.date DESC
            """, (query,))
        else:
            cursor.execute("SELECT id, date, note FROM notes ORDER BY date DESC")
        rows = cursor.fetchall()

        self.table.setRowCount(len(rows))
        for row_idx, (id_, date, note) in enumerate(rows):
            self.table.setItem(row_idx, 0, QTableWidgetItem(str(id_)))
            self.table.setItem(row_idx, 1, QTableWidgetItem(date))
            self.table.setItem(row_idx, 2, QTableWidgetItem(note))

    def edit_selected_note(self):
        selected = self.table.selectedItems()
        if not selected:
            QMessageBox.warning(self, "Ошибка", "Выберите заметку для редактирования!")
            return

        row = self.table.currentRow()
        note_id = self.table.item(row, 0).text()
        current_note = self.table.item(row, 2).text()

        new_note, ok = QInputDialog.getText(self, "Редактировать заметку", "Введите новую заметку:", text=current_note)
        if ok and new_note.strip():
            if len(new_note) > 255:
                QMessageBox.warning(self, "Ошибка", "Заметка не может превышать 255 символов!")
                return
            cursor = self.conn.cursor()
            cursor.execute("UPDATE notes SET note = ? WHERE id = ?", (new_note, note_id))
            self.conn.commit()
            self.load_notes()

    def edit_note(self, row, column):
        self.table.selectRow(row)
        self.edit_selected_note()

    def delete_note(self):
        selected = self.table.selectedItems()
        if not selected:
            QMessageBox.warning(self, "Ошибка", "Выберите заметку для удаления!")
            return

        reply = QMessageBox.question(self, "Подтверждение", "Удалить выбранную заметку?",
                                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            row = self.table.currentRow()
            note_id = self.table.item(row, 0).text()
            cursor = self.conn.cursor()
            cursor.execute("DELETE FROM notes WHERE id = ?", (note_id,))
            self.conn.commit()
            self.load_notes()

    def closeEvent(self, event):
        self.conn.close()
        event.accept()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = NotebookApp()
    window.show()
    sys.exit(app.exec_())
← Previous Next →
Back to list