Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Cách sử dụng lớp PyQt QMenu để tạo menu
Trong bài viết này, bạn sẽ học cách sử dụng lớp PyQt QMenu để tạo ra các menu cho ứng dụng của mình. QMenu là một lớp quan trọng trong PyQt, giúp bạn xây dựng các menu trong thanh menu, menu ngữ cảnh (chuột phải) hoặc các menu pop-up. Việc tạo menu không chỉ giúp ứng dụng dễ sử dụng hơn mà còn cung cấp nhiều tính năng hữu ích như mở tệp, lưu tệp, hoặc thực hiện các thao tác chỉnh sửa. Bài viết này sẽ hướng dẫn bạn từng bước cách tạo và quản lý các menu trong PyQt một cách đơn giản và hiệu quả.
Giới thiệu về lớp PyQt QMenu
Lớp QMenu
cho phép bạn tạo một menu trong thanh menu, menu ngữ cảnh (context menu), và menu bật lên (popup menu). Hướng dẫn này sẽ tập trung vào cách sử dụng QMenu
để tạo menu trong thanh menu.
Để tạo một menu và thêm nó vào thanh menu, bạn làm theo các bước sau:
- Lấy thanh menu của cửa sổ chính bằng cách gọi phương thức
menuBar()
của đối tượngQMainWindow
. - Thêm một menu vào thanh menu bằng phương thức
addMenu()
. Phương thứcaddMenu()
sẽ trả về một đối tượng mới của lớpQMenu
.
Ví dụ sau đây cho thấy cách thêm ba menu vào thanh menu của cửa sổ chính, bao gồm: File, Edit, và Help:
Bài viết này được đăng tại [free tuts .net]
menu_bar = self.menuBar() file_menu = menu_bar.addMenu('&File') edit_menu = menu_bar.addMenu('&Edit') help_menu = menu_bar.addMenu('&Help')
Lưu ý rằng dấu &
xác định một phím tắt để nhảy đến menu khi nhấn phím Alt. Ví dụ, để nhảy đến menu File, bạn nhấn tổ hợp phím Alt-F.
Khi có menu, bạn có thể thêm các mục vào nó. Thông thường, bạn tạo một QAction và sử dụng phương thức addAction()
của đối tượng QMenu để thêm các hành động vào menu.
Để thêm một dải phân cách giữa các mục menu, bạn sử dụng phương thức addSeparator()
của đối tượng QMenu.
Ví dụ về QMenu trong PyQt
Chúng ta sẽ tạo một ứng dụng chỉnh sửa văn bản để minh họa cách sử dụng lớp QMenu:
Lưu ý rằng các biểu tượng được sử dụng trong ứng dụng này từ trang web icon8.com. Bạn cũng có thể tải chúng từ đây.
Dưới đây là chương trình đầy đủ:
import sys from pathlib import Path from PyQt6.QtWidgets import QApplication, QMainWindow, QTextEdit, QFileDialog, QMessageBox, QWidget, QVBoxLayout from PyQt6.QtGui import QIcon, QAction class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setWindowIcon(QIcon('./assets/editor.png')) self.setGeometry(100, 100, 500, 300) self.title = 'Editor' self.filters = 'Text Files (*.txt)' self.path = None # Thiết lập tiêu đề cửa sổ self.set_title() # Tạo một widget QTextEdit để soạn thảo văn bản self.text_edit = QTextEdit(self) container = QWidget(self) container.setLayout(QVBoxLayout()) container.layout().addWidget(self.text_edit) self.setCentralWidget(container) # Tạo thanh menu menu_bar = self.menuBar() file_menu = menu_bar.addMenu('&File') edit_menu = menu_bar.addMenu('&Edit') help_menu = menu_bar.addMenu('&Help') # Mục menu "New" new_action = QAction(QIcon('./assets/new.png'), '&New', self) new_action.setStatusTip('Tạo tài liệu mới') new_action.setShortcut('Ctrl+N') new_action.triggered.connect(self.new_document) file_menu.addAction(new_action) # Mục menu "Open" open_action = QAction(QIcon('./assets/open.png'), '&Open...', self) open_action.triggered.connect(self.open_document) open_action.setStatusTip('Mở tài liệu') open_action.setShortcut('Ctrl+O') file_menu.addAction(open_action) # Mục menu "Save" save_action = QAction(QIcon('./assets/save.png'), '&Save', self) save_action.setStatusTip('Lưu tài liệu') save_action.setShortcut('Ctrl+S') save_action.triggered.connect(self.save_document) file_menu.addAction(save_action) file_menu.addSeparator() # Mục menu "Exit" exit_action = QAction(QIcon('./assets/exit.png'), '&Exit', self) exit_action.setStatusTip('Thoát') exit_action.setShortcut('Alt+F4') exit_action.triggered.connect(self.quit) file_menu.addAction(exit_action) # Các hành động cho menu "Edit" undo_action = QAction(QIcon('./assets/undo.png'), '&Undo', self) undo_action.setStatusTip('Hoàn tác') undo_action.setShortcut('Ctrl+Z') undo_action.triggered.connect(self.text_edit.undo) edit_menu.addAction(undo_action) redo_action = QAction(QIcon('./assets/redo.png'), '&Redo', self) redo_action.setStatusTip('Làm lại') redo_action.setShortcut('Ctrl+Y') redo_action.triggered.connect(self.text_edit.redo) edit_menu.addAction(redo_action) # Mục menu "About" trong Help about_action = QAction(QIcon('./assets/about.png'), 'About', self) help_menu.addAction(about_action) about_action.setStatusTip('Thông tin') about_action.setShortcut('F1') # Thanh trạng thái self.status_bar = self.statusBar() self.show() def set_title(self, filename=None): title = f"{filename if filename else 'Untitled'} - {self.title}" self.setWindowTitle(title) def confirm_save(self): if not self.text_edit.document().isModified(): return True message = f"Bạn có muốn lưu thay đổi trong {self.path if self.path else 'Untitled'}?" MsgBoxBtn = QMessageBox.StandardButton MsgBoxBtn = MsgBoxBtn.Save | MsgBoxBtn.Discard | MsgBoxBtn.Cancel button = QMessageBox.question(self, self.title, message, buttons=MsgBoxBtn) if button == MsgBoxBtn.Cancel: return False if button == MsgBoxBtn.Save: self.save_document() return True def new_document(self): if self.confirm_save(): self.text_edit.clear() self.set_title() def save_document(self): if self.path: return self.path.write_text(self.text_edit.toPlainText()) filename, _ = QFileDialog.getSaveFileName(self, 'Lưu tài liệu', filter=self.filters) if not filename: return self.path = Path(filename) self.path.write_text(self.text_edit.toPlainText()) self.set_title(filename) def open_document(self): filename, _ = QFileDialog.getOpenFileName(self, filter=self.filters) if filename: self.path = Path(filename) self.text_edit.setText(self.path.read_text()) self.set_title(filename) def quit(self): if self.confirm_save(): self.destroy() if __name__ == '__main__': try: import ctypes myappid = 'mycompany.myproduct.subproduct.version' ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) finally: app = QApplication(sys.argv) window = MainWindow() sys.exit(app.exec())
Cách hoạt động
Tạo cửa sổ chính bằng cách sử dụng lớp QMainWindow:
class MainWindow(QMainWindow):
Thiết lập biểu tượng và kích thước của cửa sổ:
self.setWindowIcon(QIcon('./assets/editor.png')) self.setGeometry(100, 100, 500, 300)
Khởi tạo bộ lọc tệp văn bản, tiêu đề cửa sổ và gọi phương thức set_title()
để thiết lập tiêu đề cho cửa sổ:
self.filters = 'Text Files (*.txt)' self.title = 'Editor' self.set_title()
Phương thức set_title()
nhận một tên tệp. Nếu tên tệp bị bỏ qua, phương thức set_title()
sẽ thiết lập tiêu đề của cửa sổ là Untitled - Editor
. Nếu có tên tệp, nó sẽ thiết lập tiêu đề của cửa sổ theo định dạng filename - Editor
.
Khởi tạo biến lưu đường dẫn tệp đang được mở để chỉnh sửa:
self.path = None
Lưu ý rằng chúng ta sẽ sử dụng lớp Path
từ mô-đun pathlib
để quản lý đường dẫn tệp, đọc từ tệp văn bản và ghi vào tệp văn bản.
Tạo widget QTextEdit và đặt nó làm widget chính của cửa sổ chính:
self.text_edit = QTextEdit(self) self.setCentralWidget(self.text_edit)
Tạo đối tượng QMenuBar bằng cách gọi phương thức menuBar()
của đối tượng QMainWindow:
menu_bar = self.menuBar()
Tạo các hành động mới, mở, lưu và thoát và thêm chúng vào file_menu
bằng cách sử dụng phương thức addAction()
:
# Mục menu mới new_action = QAction(QIcon('./assets/new.png'), '&New', self) new_action.setStatusTip('Create a new document') new_action.setShortcut('Ctrl+N') new_action.triggered.connect(self.new_document) file_menu.addAction(new_action)
Điều này sẽ tạo ra menu với các mục mới, mở, lưu và thoát.
Tạo các hành động undo và redo và thêm chúng vào menu chỉnh sửa:
# Menu chỉnh sửa undo_action = QAction(QIcon('./assets/undo.png'), '&Undo', self) undo_action.setStatusTip('Undo') undo_action.setShortcut('Ctrl+Z') undo_action.triggered.connect(self.text_edit.undo) edit_menu.addAction(undo_action) redo_action = QAction(QIcon('./assets/redo.png'), '&Redo', self) redo_action.setStatusTip('Redo') redo_action.setShortcut('Ctrl+Y') redo_action.triggered.connect(self.text_edit.redo) edit_menu.addAction(redo_action)
Tạo hành động "About" và thêm nó vào menu Help:
about_action = QAction(QIcon('./assets/about.png'), 'About', self) help_menu.addAction(about_action) about_action.setStatusTip('About') about_action.setShortcut('F1')
Thêm thanh trạng thái vào cửa sổ chính bằng cách sử dụng phương thức statusBar()
của đối tượng QMainWindow:
self.status_bar = self.statusBar()
Định nghĩa phương thức confirm_save()
để yêu cầu người dùng lưu tài liệu hoặc không. Nếu người dùng nhấp vào nút "Yes", gọi phương thức save_document()
để lưu văn bản từ widget QTextEdit vào tệp:
def confirm_save(self): if not self.text_edit.document().isModified(): return True message = f"Do you want to save changes to {self.path if self.path else 'Untitled'}?" MsgBoxBtn = QMessageBox.StandardButton MsgBoxBtn = MsgBoxBtn.Save | MsgBoxBtn.Discard | MsgBoxBtn.Cancel button = QMessageBox.question( self, self.title, message, buttons=MsgBoxBtn ) if button == MsgBoxBtn.Cancel: return False if button == MsgBoxBtn.Save: self.save_document() return True
Định nghĩa phương thức new_document()
để chạy khi người dùng chọn mục New trong menu:
def new_document(self): if self.confirm_save(): self.text_edit.setText('') self.set_title()
Định nghĩa phương thức save_document()
để lưu văn bản của widget QTextEdit vào một tệp văn bản:
def save_document(self): if (self.path): return self.path.write_text(self.text_edit.toPlainText()) filename, _ = QFileDialog.getSaveFileName( self, 'Save File', filter=self.filters ) if not filename: return self.path = Path(filename) self.path.write_text(self.text_edit.toPlainText()) self.set_title(filename)
Định nghĩa phương thức open_document()
để hiển thị hộp thoại Mở tệp và tải nội dung từ tệp văn bản vào widget QTextEdit:
def open_document(self): filename, _ = QFileDialog.getOpenFileName(self, filter=self.filters) if filename: self.path = Path(filename) self.text_edit.setText(self.path.read_text()) self.set_title(filename)
Định nghĩa phương thức quit()
để chạy khi người dùng chọn mục Exit trong menu:
def quit(self): if self.confirm_save(): self.destroy()
Cuối cùng, nếu bạn chạy chương trình trên Windows, thanh tác vụ sẽ không hiển thị biểu tượng cửa sổ chính chính xác. Để khắc phục điều này, bạn sử dụng mã sau:
import ctypes myappid = 'mycompany.myproduct.subproduct.version' ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
Nếu bạn thực thi chương trình trên macOS hoặc Linux, mã này sẽ gây lỗi nhập khẩu. Do đó, chúng tôi bọc nó trong khối try
:
try: import ctypes myappid = 'mycompany.myproduct.subproduct.version' ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) finally: app = QApplication(sys.argv) window = MainWindow() sys.exit(app.exec())
Kết bài
Trong PyQt, lớp QMenu
là công cụ chính để tạo và quản lý các menu trong ứng dụng. Khi bạn làm việc với ứng dụng có giao diện người dùng, việc sử dụng QMenu
giúp bạn dễ dàng tổ chức các tùy chọn và hành động cho người dùng.
Để thêm menu vào thanh menu chính của cửa sổ ứng dụng, bạn sử dụng phương thức menuBar()
của lớp QMainWindow
để tạo một thanh menu. Sau đó, phương thức addMenu()
cho phép bạn thêm các menu vào thanh menu đó. Mỗi menu có thể chứa nhiều mục khác nhau, được thêm vào bằng cách sử dụng phương thức addAction()
của đối tượng QMenu
.
Việc sử dụng QMenu
không chỉ giúp cải thiện cấu trúc giao diện ứng dụng mà còn tạo ra một trải nghiệm người dùng thân thiện và dễ dàng điều hướng. Thực hiện đúng cách, bạn có thể tạo ra các ứng dụng với giao diện menu phong phú và chức năng tùy chỉnh để đáp ứng nhu cầu của người dùng.