import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QMimeData
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QAction, QFileDialog, QMessageBox,\
QFontDialog, QColorDialog
class Demo(QMainWindow):
# В начале класса мы устанавливаем несколько переменных класса, которые используются
# для определения того, был ли сохранен текст, сохраняется ли это в первый раз,
# и для сохранения пути к файлу:
is_saved = True
is_saved_first = True
path = ''
def __init__(self):
super(Demo, self).__init__()
# Затем мы начинаем добавлять строку меню, панель инструментов и строку состояния:
self.file_menu = self.menuBar().addMenu('File')
self.edit_menu = self.menuBar().addMenu('Edit')
self.help_menu = self.menuBar().addMenu('Help')
self.file_toolbar = self.addToolBar('File')
self.edit_toolbar = self.addToolBar('Edit')
self.status_bar = self.statusBar()
# Далее нам нужно только добавить различные действия к строке меню и панели инструментов,
# то есть QAction. Этот класс обычно используется с панелью меню и панелью инструментов.
# Вы можете думать о действии как о команде. Всякий раз, когда пользователь нажимает
# на действие, запускается определенная команда, и программа выполняет соответствующую команду.
# Теперь нам нужно создать несколько действий:
self.new_action = QAction('New', self)
self.open_action = QAction('Open', self)
self.save_action = QAction('Save', self)
self.save_as_action = QAction('Save As', self)
self.close_action = QAction('Close', self)
self.cut_action = QAction('Cut', self)
self.copy_action = QAction('Copy', self)
self.paste_action = QAction('Paste', self)
self.font_action = QAction('Font', self)
self.color_action = QAction('Color', self)
self.about_action = QAction('Qt', self)
# Поскольку это Notepad, должен быть текстовый редактор, поэтому, конечно,
# в функции инициализации класса должен быть создан экземпляр элемента управления QTextEdit:
self.text_edit = QTextEdit(self)
# С помощью функции вырезания и вставки мы естественно думаем
# о буфере обмена и классе QMimeData:
self.mime_data = QMimeData() # QMimeData
self.clipboard = QApplication.clipboard() # буфере обмена
# Вызвать метод setCentralWidget() класса QMainWindow, чтобы установить
# центральный элемент управления главного окна,
# в котором мы устанавливаем поле редактирования текста text_edit для центрального
# элемента управления.
self.setCentralWidget(self.text_edit)
# Вызовите метод resize(), чтобы установить для окна соответствующий размер;
self.resize(450, 600)
self.menu_init()
# После того, как строка меню установлена, мы можем установить панель инструментов:
self.toolbar_init()
# Настройка строки состояния очень проста.
self.status_bar_init()
self.action_init() # установить действия
self.text_edit_int()
def menu_init(self):
""" добавляем соответствующее действие в строку меню: """
# Видно, что мы можем добавлять к нему действия, вызывая метод addAction().
# Метод addSeparator(), как следует из названия, добавляет разделитель, что означает,
# что close_action отделено от четырех вышеуказанных действий, что выглядит более организованным.
self.file_menu.addAction(self.new_action)
self.file_menu.addAction(self.open_action)
self.file_menu.addAction(self.save_action)
self.file_menu.addAction(self.save_as_action)
self.file_menu.addSeparator()
self.file_menu.addAction(self.close_action)
self.edit_menu.addAction(self.cut_action)
self.edit_menu.addAction(self.copy_action)
self.edit_menu.addAction(self.paste_action)
self.edit_menu.addSeparator()
self.edit_menu.addAction(self.font_action)
self.edit_menu.addAction(self.color_action)
self.help_menu.addAction(self.about_action)
def toolbar_init(self):
self.file_toolbar.addAction(self.new_action)
self.file_toolbar.addAction(self.open_action)
self.file_toolbar.addAction(self.save_action)
self.file_toolbar.addAction(self.save_as_action)
self.edit_toolbar.addAction(self.cut_action)
self.edit_toolbar.addAction(self.copy_action)
self.edit_toolbar.addAction(self.paste_action)
self.edit_toolbar.addAction(self.font_action)
self.edit_toolbar.addAction(self.color_action)
def status_bar_init(self):
# Нам просто нужно вызвать метод showMessage() и передать состояние,
# которое программа хочет отобразить при открытии программы.
self.status_bar.showMessage('Ready to compose')
def action_init(self):
# Передайте параметр QIcon через метод setIcon(), чтобы установить значок действия.
# Метод setShortCut() используется для установки сочетания клавиш.
# Здесь мы устанавливаем сочетание клавиш нового действия на Ctrl + N.
# Тогда нажатие Ctrl+N эквивалентно нажатию этого действия (независимо от платформы,
# здесь используется Ctrl равномерно. (Даже на Mac, но после запуска программы пользователи
# Mac должны нажать Ctrl + N).
# Метод setToolTip() можно использовать для установки небольшого всплывающего приглашения.
# Когда указатель мыши находится над действием, будет отображаться соответствующее
# приглашение (конечно, мы также можем использовать этот метод для других объектов,
# таких как QPushButton).
# Метод setStatusTip() предназначен для установки информации в строке состояния.
# Когда курсор мыши находится над действием, строка состояния отображает
# соответствующую информацию.
# Наконец, мы связываем сработавший сигнал new_action с пользовательской функцией слота:
self.new_action.setIcon(QIcon('images/new.ico'))
self.new_action.setShortcut('Ctrl+N')
self.new_action.setToolTip('Create a new file')
self.new_action.setStatusTip('Create a new file')
self.new_action.triggered.connect(self.new_func)
self.open_action.setIcon(QIcon('images/open.png'))
self.open_action.setShortcut('Ctrl+O')
self.open_action.setToolTip('Open an existing file')
self.open_action.setStatusTip('Open an existing file')
self.open_action.triggered.connect(self.open_file_func)
self.save_action.setIcon(QIcon('images/save.png'))
self.save_action.setShortcut('Ctrl+S')
self.save_action.setToolTip('Save the file')
self.save_action.setStatusTip('Save the file')
self.save_action.triggered.connect(lambda: self.save_func(self.text_edit.toHtml()))
self.save_as_action.setIcon(QIcon('img/save_as.png'))
self.save_as_action.setShortcut('Ctrl+A')
self.save_as_action.setToolTip('Save the file to a specified location')
self.save_as_action.setStatusTip('Save the file to a specified location')
self.save_as_action.triggered.connect(lambda: self.save_as_func(self.text_edit.toHtml()))
self.close_action.setIcon(QIcon('img/exit.png')) # close.ico
self.close_action.setShortcut('Ctrl+E')
self.close_action.setToolTip('Close the window')
self.close_action.setStatusTip('Close the window')
self.close_action.triggered.connect(self.close_func)
self.cut_action.setIcon(QIcon('img/cut.png'))
self.cut_action.setShortcut('Ctrl+X')
self.cut_action.setToolTip('Cut the text to clipboard')
self.cut_action.setStatusTip('Cut the text')
self.cut_action.triggered.connect(self.cut_func)
self.copy_action.setIcon(QIcon('img/copy.png'))
self.copy_action.setShortcut('Ctrl+C')
self.copy_action.setToolTip('Copy the text')
self.copy_action.setStatusTip('Copy the text')
self.copy_action.triggered.connect(self.copy_func)
self.paste_action.setIcon(QIcon('img/paste.png'))
self.paste_action.setShortcut('Ctrl+V')
self.paste_action.setToolTip('Paste the text')
self.paste_action.setStatusTip('Paste the text')
self.paste_action.triggered.connect(self.paste_func)
self.font_action.setIcon(QIcon('img/font.png'))
self.font_action.setShortcut('Ctrl+T')
self.font_action.setToolTip('Change the font')
self.font_action.setStatusTip('Change the font')
self.font_action.triggered.connect(self.font_func)
self.color_action.setIcon(QIcon('img/color.png'))
self.color_action.setShortcut('Ctrl+R')
self.color_action.setToolTip('Change the color')
self.color_action.setStatusTip('Change the color')
self.color_action.triggered.connect(self.color_func)
self.about_action.setIcon(QIcon('img/about.png'))
self.about_action.setShortcut('Ctrl+Q')
self.about_action.setToolTip('What is Qt?')
self.about_action.setStatusTip('What is Qt?')
self.about_action.triggered.connect(self.about_func)
# В конце у нас остается только поле ввода текста QTextEdit, оставленное неустановленным.
# Подключите сигнал textChanged к функции слота.
# В функции slot(text_changed_func) мы можем установить переменную self.is_saved:
def text_edit_int(self):
self.text_edit.textChanged.connect(self.text_changed_func)
def text_changed_func(self):
if self.text_edit.toPlainText():
self.is_saved = False
else:
self.is_saved = True
# Перед созданием нового файла нам нужно определить, сохранен ли текущий текст.
# Если нет, появится всплывающее окно с вопросом, хотите ли вы сохранить его.
# Если вы нажмете Да, вы вызовете функцию save_func() для его сохранения.
# Если вы нажмете Нет, чтобы сохранить, он будет очищен.
# Если вы нажмете `Отмена` для отмены, никакие действия не будут предприняты,
# если они были сохранены, вы можете просто очистить поле для редактирования текста;
def new_func(self):
if not self.is_saved:
choice = QMessageBox.question(self, '', 'Do you want to save the text?',
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if choice == QMessageBox.Yes:
self.save_func()
self.text_edit.clear()
elif choice == QMessageBox.No:
self.text_edit.clear()
else:
pass
else:
self.text_edit.clear()
# Метод установки действия open_action похож, мы в основном рассмотрим функцию подключенного слота:
# Логика очень похожа на новое действие, за исключением того, что операция очистки поля
# редактирования текста заменяется операцией открытия диалогового окна файла;
def open_file_func(self):
if not self.is_saved:
choice = QMessageBox.question(self, '', 'Do you want to save the text?',
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if choice == QMessageBox.Yes:
self.save_func()
file, _ = QFileDialog.getOpenFileName(self, 'Open File', './', 'Files (*.html *.txt *.log)')
if file:
with open(file, 'r') as f:
self.text_edit.clear()
self.text_edit.setText(f.read())
self.is_saved = True
elif choice == QMessageBox.No:
file, _ = QFileDialog.getOpenFileName(self, 'Open File', './', 'Files (*.html *.txt *.log)')
if file:
with open(file, 'r') as f:
self.text_edit.clear()
self.text_edit.setText(f.read())
self.is_saved = True
else:
pass
else:
file, _ = QFileDialog.getOpenFileName(self, 'Open File', './', 'Files (*.html *.txt *.log)')
if file:
with open(file, 'r') as f:
self.text_edit.clear()
self.text_edit.setText(f.read())
self.is_saved = True
# Функции слотов
def save_func(self, text):
if self.is_saved_first:
self.save_as_func(text)
else:
with open(self.path, 'w') as f:
f.write(text)
self.is_saved = True
def save_as_func(self, text):
self.path, _ = QFileDialog.getSaveFileName(self, 'Save File', './', 'Files (*.html *.txt *.log)')
if self.path:
with open(self.path, 'w') as f:
f.write(text)
self.is_saved = True
self.is_saved_first = False
# Функция close_func() ниже аналогична методу реализации события закрытия окна.
# Обратите внимание, что поскольку в нашем блокноте используется цвет, мы не можем вызвать
# метод toPlainText() класса QTextEdit, поскольку этот метод получает чистый текст,
# цвет будет потерян. Метод toHtml() должен быть вызван для сохранения цвета;
def close_func(self):
if not self.is_saved:
choice = QMessageBox.question(self, 'Save File', 'Do you want to save the text?',
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if choice == QMessageBox.Yes:
self.save_func(self.text_edit.toHtml())
self.close()
elif choice == QMessageBox.No:
self.close()
else:
pass
def closeEvent(self, QCloseEvent):
if not self.is_saved:
choice = QMessageBox.question(self, 'Save File', 'Do you want to save the text?',
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if choice == QMessageBox.Yes:
self.save_func(self.text_edit.toHtml())
QCloseEvent.accept()
elif choice == QMessageBox.No:
QCloseEvent.accept()
else:
QCloseEvent.ignore()
# Функция слота, связанная с помощью cut_action, выглядит следующим образом:
# Метод self.text_edit.textCursor() может получить текущий указатель
# текстового поля редактирования (тип QTextCursor), а затем вызвать метод selection(),
# чтобы получить содержимое, в настоящий момент выбранное указателем, но типом в данный
# момент является QTextDocumentFragment, мы Вам нужно вызвать метод toHtml(),
# чтобы получить текстовое содержимое.
# Когда пользователь режет, обрезанный текст должен исчезнуть,
# поэтому вызовите метод removeSelectedText() QTextCursor.
def cut_func(self):
self.mime_data.setHtml(self.text_edit.textCursor().selection().toHtml())
self.clipboard.setMimeData(self.mime_data)
self.text_edit.textCursor().removeSelectedText()
# Функция слота copy_action выглядит следующим образом, метод тот же, за исключением того,
# что текст не нужно удалять в это время:
def copy_func(self):
self.mime_data.setHtml(self.text_edit.textCursor().selection().toHtml())
self.clipboard.setMimeData(self.mime_data)
# В слоте функции paste_action нам просто нужно вызвать метод insetHtml()
# для вставки текста в буфер обмена (этот метод вставит текст в позицию указателя):
def paste_func(self):
self.text_edit.insertHtml(self.clipboard.mimeData().html())
def font_func(self):
font, ok = QFontDialog.getFont()
if ok:
self.text_edit.setFont(font)
def color_func(self):
color = QColorDialog.getColor()
if color.isValid():
self.text_edit.setTextColor(color)
# Слот-функция, связанная с about_action, является самой простой,
# просто откройте окно сообщения о Qt:
def about_func(self):
QMessageBox.aboutQt(self, 'About Qt')
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
主窗口
QMainWindow
在更大、更复杂、更强大的应用程序中,我们通常继承自QMainWindow
开发类。这个主窗口为构建应用程序的用户界面提供了很好的基础。主窗口类
QMainWindow
为我们提供菜单栏,
Menu Bar
工具栏,
Toolbars
小部件
Dock
和状态栏,
Status Bar
我们可以根据需要添加任意数量。此外,它使我们能够快速开发功能复杂且方便的应用程序。
Блокнот
/记事本 一个简单的记事本应用程序,了解如何使用QMainWindow