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_())
[file-name 000248_2025-10-02_08-44-15.txt]