1. 程式人生 > >《快速掌握PyQt5》第二十四章 主視窗QMainWindow

《快速掌握PyQt5》第二十四章 主視窗QMainWindow

第二十四章 主視窗QMainWindow

在較為大型複雜,功能較多的應用程式中,我們通常繼承QMainWindow類來進行開發。該主視窗為搭建應用使用者介面提供了非常好的框架,請看下圖:

可以看出該主視窗類為我們提供了選單欄(Menu Bar)、工具欄(Tool Bar)、控制元件停靠區域(Dock Widgets)和狀態列(Status Bar),我們可以往其中加入很多自己想要的東西,這也使得我們可以快速地開發一個功能複雜並且介面友好的應用程式。

本章主要介紹選單欄、工具欄和狀態列,控制元件停靠區域會放在後續章節。

24.1 記事本應用

接下來我們完成一個簡易的記事本應用來了解一下QMainWindow的用法(其實就是對上一章23.2節中的程式再進行擴充套件):

(Mac上的選單欄在螢幕最上方)

為保證程式碼敲擊流暢,請先在程式中匯入一下模組:

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

接著我們開始新增選單欄、工具欄和狀態列:

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,該類通常與選單欄和工具欄搭配使用。可以將一個動作看作一種命令,每當使用者點選某個動作時,就會觸發某種命令,程式從而執行相應的命令。那現在我們就要例項化幾個動作:

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

        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)

這裡一共例項化了11個動作,分別為:新建new_action、開啟open_action、儲存save_action、另存為save_as_action、關閉close_action、剪下cut_action、複製copy_action、貼上paste_action、字型改變font_action、顏色改變color_action以及關於about_action。

既然是記事本的話,那肯定要有文字編輯器,所以在類的初始化函式中當然還要例項化一個QTextEdit控制元件:

self.text_edit = QTextEdit(self)

有剪下複製貼上功能的話就讓我們自然而然的想到了剪貼簿以及QMimeData類 :

self.mime_data = QMimeData()
self.clipboard = QApplication.clipboard()

接下來我們往初始化函式中再新增以下程式碼:

self.setCentralWidget(self.text_edit)    # 1
self.resize(450, 600)

self.menu_init()                         # 2
self.toolbar_init()
self.status_bar_init()
self.action_init()
self.text_edit_int()

 1. 呼叫QMainWindow的setCentralWidget()方法可以設定主視窗的中央控制元件,這裡我們將文字編輯框text_edit設定為中央控制元件。呼叫resize()方法將視窗設定到合適的大小;

2. 在程式碼量比較多的情況下,將各個物件分開來設定會讓程式碼更加清晰,如果都同時擠在__init__()中的話會顯得非常混亂,也不方便日後維護。

我們接下來看一下menu_init()函式,在這裡我們將相應的動作新增到了選單欄中:

def menu_init(self):
    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)

可以看出我們呼叫addAction()方法就可以將動作新增進去。addSeparator()方法顧名思義就是加一個分割條,也就是讓close_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)

接著是狀態列,狀態列設定非常簡單,我們只需要呼叫showMessage()方法,傳入程式開啟時想要顯示的狀態即可(讀者可以回到上方看看狀態列剛開始的樣子):

def status_bar_init(self):
    self.status_bar.showMessage('Ready to compose')

選單欄、工具欄和狀態列都設定好了,但是各個動作還只是個空殼,現在點選動作後不會有任何反應,所以接下來我們要在action_init()中設定動作:

def action_init(self):
    self.new_action.setIcon(QIcon('images/new.ico'))            # 1
    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.ico'))          # 2
    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.ico'))          # 3
    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('images/save_as.ico'))    # 4
    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('images/close.ico'))        # 5
    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('images/cut.ico'))            # 6
    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('images/copy.ico'))          # 7
    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('images/paste.ico'))        # 8
    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('images/font.ico'))          # 9
    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('images/color.ico'))        # 10
    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('images/about.ico'))        # 11
    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)

可以看出各個動作設定方法非常類似:

所有圖片我已經放在了百度雲中,讀者可以自行下載:

連結: https://pan.baidu.com/s/1Pt0yirPsOmRIPkELjCFScw 提取碼: d8gm

1. 通過setIcon()方法傳入QIcon引數來設定動作的圖示。setShortCut()方法就是用來設定快捷鍵的,這裡我們將新建動作的快捷鍵設定為Ctrl+N,那麼按下Ctrl+N就相當於點選了這個動作了(不管什麼平臺,這裡統一用Ctrl,即便是在Mac上,不過程式執行後,使用Mac的使用者要按下的是Command+N)。setToolTip()方法可以用來設定小氣泡提示,當滑鼠停留在該動作上時,就會顯示相應的提示(當然我們也可以對其他物件使用該方法,比如QPushButton)。setStatusTip()就是設定狀態列資訊,當滑鼠停留在該動作上時,狀態列會顯示相應的資訊。最後我們將new_action的triggered訊號與自定義的槽函式連線起來:

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

新建前我們要判斷當前文字是否有儲存,如果沒有的話,那就出現彈框詢問是否要儲存,按Yes的話就呼叫save_func()函式進行儲存(save_func()等函式筆者已經在上一章中詳細講解了,這裡不再複述),儲存好了當然要將當前的文字編輯框清空好方便讓使用者重新寫作。若按No不進行儲存的話,就直接清空。若按下Cancel取消的話,則不進行任何動作;如果已經儲存了,那麼就直接清空文字編輯框就行了;

2. 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

邏輯與新建動作非常相似,只不過就是將清空文字編輯框操作換成了開啟檔案對話方塊操作;

3-5. 這幾個動作的槽函式都已在上一章中講解過,相信讀者已經完全可以理解了,這裡就直接放出程式碼:

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()跟我們在上一章中講的視窗關閉事件實現方法類似,只不過就是將QCloseEvent.accept()換成了self.close()。QCloseEvent.ignoret()其實功能上就相當於pass:

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

請注意,由於我們的記事本涉及到顏色,所以不能呼叫QTextEdit的toPlainText()方法,因為該方法獲取的是純文字,所以顏色會丟失掉。應該要呼叫toHtml()方法保留顏色;

6. cut_action連線的槽函式如下:

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

self.text_edit.textCursor()方法可以獲取到文字編輯框當前的指標(型別為QTextCursor),此時再呼叫selection()方法可以獲取到指標當前所選擇的內容,但此時的型別為QTextDocumentFragment,我們需要再呼叫toHtml()方法來獲取到文字內容。當用戶進行剪下後,被剪下的文字肯定要消失,所以就呼叫QTextCursor的removeSelectedText()方法。

7. copy_action的槽函式如下,方法同理,只不過此時不需要將文字刪除。:

def copy_func(self):
    self.mime_data.setHtml(self.text_edit.textCursor().selection().toHtml())
    self.clipboard.setMimeData(self.mime_data)

8. 在paste_action的槽函式中,我們只需要呼叫insetHtml()方法將剪貼簿中的文字插入即可(該方法會在指標位置插入文字):

def paste_func(self):
    self.text_edit.insertHtml(self.clipboard.mimeData().html())

9-10. font_action和color_action的槽函式在上一章中也已經講過了,這裡不再複述:

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)

11. about_action所連線的槽函式最為簡單,就是開啟一個關於Qt的訊息框即可:

def about_func(self):
    QMessageBox.aboutQt(self, 'About Qt')

最後我們只剩下QTextEdit文字編輯框沒有設定了,同上一章一樣,將textChanged訊號和槽函式連線。在槽函式中,我們對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

執行截圖如下,選單欄截圖:

注:

  • Mac上若QAction的文字設為Quit或者Exit時,無法被新增(文字無法隨意定);
  • Mac上Edit選單下會自動加入Start Dictation和Emoji&Symbols動作;
  • Mac上Help選單下會自動加入Search搜尋框。

Tool Tip和Status Tip:

完整程式碼如下:

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

        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)

        self.text_edit = QTextEdit(self)

        self.mime_data = QMimeData()
        self.clipboard = QApplication.clipboard()

        self.setCentralWidget(self.text_edit)
        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):
        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):
        self.status_bar.showMessage('Ready to compose')

    def action_init(self):
        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.ico'))
        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.ico'))
        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('images/save_as.ico'))
        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('images/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('images/cut.ico'))
        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('images/copy.ico'))
        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('images/paste.ico'))
        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('images/font.ico'))
        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('images/color.ico'))
        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('images/about.ico'))
        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)

    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

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

    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

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

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

    def copy_func(self):
        self.mime_data.setHtml(self.text_edit.textCursor().selection().toHtml())
        self.clipboard.setMimeData(self.mime_data)

    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)

    def about_func(self):
        QMessageBox.aboutQt(self, 'About Qt')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

24.2 小結

1. 通過上方例項我們知道可以往QMainWindow主視窗中新增很多功能,各個功能通過QAction動作物件來實現。而這寫動作物件被整齊有序的新增到選單欄和工具欄中。這就是為什麼我們應該用QMainWindow類來實現較為複雜的應用程式;

2. 關鍵知識點:獲取選中的文字 self.text_edit.textCursor().selection().toHtml(),讀者可以在QtAssitant中查詢下相應方法。

----------------------------------------------------------------------

喜歡的小夥伴可以加入這個Python QQ交流群一起學習:820934083