• 用 eric6 与 PyQt5 实现python的极速GUI编程(系列04)---- PyQt5自带教程:地址簿(address book)


    【引子】

    在PyQt5自带教程中,地址簿(address book)程序没有完全实现界面与业务逻辑分离。

    本文我打算用eric6+PyQt5对其进行改写,以实现界面与逻辑完全分离。

    【概览】


    1、界面:


    2、功能简介:
    程序有三种操作模式:浏览模式、添加模式、编辑模式。 其实现的功能都显式的体现在各个按钮上


    3、主要步骤:
    1)、在eric6中新建项目,新建窗体,取名为 addressbook.ui 文件

    2)、(自动打开)进入PyQt5 Desinger,编辑图形界面,保存

    3)、回到eric 6,对上一步得到的界面文件 addressbook.ui 文件右击,编译窗体,得到 Ui_addressbook.py 文件

    4)、然后再对 addressbook.ui 文件右击,生成对话框代码,得到 addressbook.py 文件。(在addressbook.py中添加自己的程序逻辑)

    5)、py2exe打包成exe文件(此步略)


    4、涉及的知识点:
    import sys, pickle

    from PyQt5.QtCore import pyqtSlot, QFile, QIODevice, Qt, QTextStream
    from PyQt5.QtWidgets import QWidget, QDialog, QLabel, QLineEdit, QPushButton, QHBoxLayout,  QMessageBox, QFileDialog,  QApplication



    【正文】
    1、一般的步骤省略不表,接上面主要步骤第二步:

    在Qt设计师中,将行编辑框(lineEdit)、文本编辑框(textEdit)、及十一个按钮(pushButton)的对象名(objectName)分别设置如下:

    lineEdit_name(姓名输入框)

    textEdit_address(地址输入框)

    pushButton_add(添加 按钮)

    pushButton_edit(编辑 按钮)

    pushButton_remove(删除 按钮)

    pushButton_find(查找 按钮)

    pushButton_submit(提交 按钮)

    pushButton_cancel(取消 按钮)

    pushButton_load(导入 按钮)

    pushButton_save(保存 按钮)

    pushButton_export(导出 按钮)

    pushButton_previous(前一个 按钮)

    pushButton_next(后一个 按钮)

    2、关闭Qt设计师,回到eric6

    先右击addressbook.ui 文件,编译窗体,得到 Ui_addressbook.py 文件

    然后再次右击addressbook.ui 文件,生成对话框代码,

    在弹窗中勾选十一个按钮的 on_x_clicked() 事件,确定,得到 addressbook.py 文件。

    3、对addressbook.py 文件执行下面四步处理

    1)、清空所有注释

    2)、去掉一个多余的点,将

    from .Ui_addressbook import Ui_Form

    变成:

    from Ui_addressbook import Ui_Form

    3)、将所有clicked()下的代码改写为pass

        @pyqtSlot()
        def on_pushButton_add_clicked(self):
            pass
        
        @pyqtSlot()
        def on_pushButton_edit_clicked(self):
            pass
        
        # ...

    4)、在 addressbook.py 文件最后面加上下面几句代码:

    if __name__ == '__main__':
        import sys
        from PyQt5.QtWidgets import QApplication
        
        app = QApplication(sys.argv)
        dlg = Dialog()
        dlg.show()
        sys.exit(app.exec_())

    最后,addressbook.py 看起来是这个样子:

     1 # -*- coding: utf-8 -*-
     2 
     3 from PyQt5.QtCore import pyqtSlot
     4 from PyQt5.QtWidgets import QDialog
     5 
     6 from Ui_addressbook import Ui_Dialog
     7 
     8 
     9 class Dialog(QDialog, Ui_Dialog):
    10     def __init__(self, parent=None):
    11         super(Dialog, self).__init__(parent)
    12         self.setupUi(self)
    13     
    14     @pyqtSlot()
    15     def on_pushButton_add_clicked(self):
    16         pass
    17     
    18     @pyqtSlot()
    19     def on_pushButton_edit_clicked(self):
    20         pass
    21     
    22     @pyqtSlot()
    23     def on_pushButton_remove_clicked(self):
    24         pass
    25     
    26     @pyqtSlot()
    27     def on_pushButton_find_clicked(self):
    28         pass
    29     
    30     @pyqtSlot()
    31     def on_pushButton_submit_clicked(self):
    32         pass
    33     
    34     @pyqtSlot()
    35     def on_pushButton_cancel_clicked(self):
    36         pass
    37     
    38     @pyqtSlot()
    39     def on_pushButton_load_clicked(self):
    40         pass
    41     
    42     @pyqtSlot()
    43     def on_pushButton_save_clicked(self):
    44         pass
    45     
    46     @pyqtSlot()
    47     def on_pushButton_export_clicked(self):
    48         pass
    49     
    50     @pyqtSlot()
    51     def on_pushButton_previous_clicked(self):
    52         pass
    53     
    54     @pyqtSlot()
    55     def on_pushButton_next_clicked(self):
    56         pass
    57 
    58 if __name__ == '__main__':
    59     import sys
    60     from PyQt5.QtWidgets import QApplication
    61     
    62     app = QApplication(sys.argv)
    63     dlg = Dialog()
    64     dlg.show()
    65     sys.exit(app.exec_())
    View Code

    4、下面添加逻辑代码

    # -*- coding: utf-8 -*-
    
    import pickle
    
    from PyQt5.QtCore import pyqtSlot, QFile, QIODevice, Qt, QTextStream
    from PyQt5.QtWidgets import QWidget, QDialog, QLabel, QLineEdit, QPushButton, QHBoxLayout,  QMessageBox, QFileDialog
    
    
    from Ui_addressbook import Ui_Dialog
    
    
    class SortedDict(dict):
        class Iterator(object):
            def __init__(self, sorted_dict):
                self._dict = sorted_dict
                self._keys = sorted(self._dict.keys())
                self._nr_items = len(self._keys)
                self._idx = 0
    
            def __iter__(self):
                return self
    
            def next(self):
                if self._idx >= self._nr_items:
                    raise StopIteration
    
                key = self._keys[self._idx]
                value = self._dict[key]
                self._idx += 1
    
                return key, value
    
            __next__ = next
    
        def __iter__(self):
            return SortedDict.Iterator(self)
    
        iterkeys = __iter__
    
    class FindDialog(QDialog):
        def __init__(self, parent=None):
            super(FindDialog, self).__init__(parent)
    
            findLabel = QLabel('输入要查找的联系人姓名:')
            self.lineEdit = QLineEdit()
    
            self.findButton = QPushButton('查找')
            self.findText = ''
    
            layout = QHBoxLayout()
            layout.addWidget(findLabel)
            layout.addWidget(self.lineEdit)
            layout.addWidget(self.findButton)
    
            self.setLayout(layout)
            self.setWindowTitle('查找联系人')
    
            self.findButton.clicked.connect(self.findClicked)
            self.findButton.clicked.connect(self.accept)
    
        def findClicked(self):
            text = self.lineEdit.text()
    
            if not text:
                QMessageBox.information(self, '姓名不能为空', '请输入一个姓名')
                return
    
            self.findText = text
            self.lineEdit.clear()
            self.hide()
    
        def getFindText(self):
            return self.findText
    
    
    class Dialog(QDialog, Ui_Dialog):
        NavigationMode, AddingMode, EditingMode = range(3)
        
        def __init__(self, parent=None):
            super(Dialog, self).__init__(parent)
            self.setupUi(self)
            
            self.pushButton_submit.hide()
            self.pushButton_cancel.hide()
            
            self.contacts = SortedDict()
            self.oldName = ''
            self.oldAddress = ''
            self.currentMode = self.NavigationMode
            
            self.dialog = FindDialog()
        
        @pyqtSlot()
        def on_pushButton_add_clicked(self):
            self.oldName = self.lineEdit_name.text()
            self.oldAddress = self.textEdit_address.toPlainText()
            
            self.lineEdit_name.clear()
            self.textEdit_address.clear()
            
            self.updateInterface(self.AddingMode)
            
        @pyqtSlot()
        def on_pushButton_edit_clicked(self):
            self.oldName = self.lineEdit_name.text()
            self.oldAddress = self.textEdit_address.toPlainText()
    
            self.updateInterface(self.EditingMode)
        
        @pyqtSlot()
        def on_pushButton_remove_clicked(self):
            name = self.lineEdit_name.text()
            address = self.textEdit_address.toPlainText()
    
            if name in self.contacts:
                button = QMessageBox.question(self, '确定删除','你真的确定要删除 {} 吗?'.format(name), QMessageBox.Yes | QMessageBox.No)
    
                if button == QMessageBox.Yes:
                    self.on_pushButton_previous_clicked()
                    del self.contacts[name]
    
                    QMessageBox.information(self, '删除成功','{}已经从你的地址簿删除了!'.format(name))
    
            self.updateInterface(self.NavigationMode)
        
        @pyqtSlot()
        def on_pushButton_find_clicked(self):
            self.dialog.show()
    
            if self.dialog.exec_() == QDialog.Accepted:
                contactName = self.dialog.getFindText()
    
                if contactName in self.contacts:
                    self.lineEdit_name.setText(contactName)
                    self.textEdit_address.setText(self.contacts[contactName])
                else:
                    QMessageBox.information(self, '找不到','抱歉,{}不在你的地址簿内!'.format(contactName))
                    return
    
            self.updateInterface(self.NavigationMode)
        
        @pyqtSlot()
        def on_pushButton_submit_clicked(self):
            name = self.lineEdit_name.text()
            address = self.textEdit_address.toPlainText()
    
            if name == "" or address == "":
                QMessageBox.information(self, '不能为空', '请输入姓名及地址!')
                return
    
            if self.currentMode == self.AddingMode:
                if name not in self.contacts:
                    self.contacts[name] = address
                    QMessageBox.information(self, '添加成功', '{} 已经添加到你的地址簿!'.format(name))
                else:
                    QMessageBox.information(self, '添加失败', '{} 已经存在于你的地址簿!'.format(name))
                    return
    
            elif self.currentMode == self.EditingMode:
                if self.oldName != name:
                    if name not in self.contacts:
                        QMessageBox.information(self, '编辑成功','{} 已经被编辑到你的地址簿!'.format(self.oldName))
                        
                        del self.contacts[self.oldName]
                        self.contacts[name] = address
                    else:
                        QMessageBox.information(self, '编辑失败','抱歉,{} 已经存在于你的地址簿!'.format(name))
                        return
                elif self.oldAddress != address:
                    QMessageBox.information(self, '编辑成功','{} 已经被编辑到你的地址簿!'.format(name))
                    self.contacts[name] = address
    
            self.updateInterface(self.NavigationMode)
        
        @pyqtSlot()
        def on_pushButton_cancel_clicked(self):
            self.lineEdit_name.setText(self.oldName)
            self.textEdit_address.setText(self.oldAddress)
            
            self.updateInterface(self.NavigationMode)
            
        @pyqtSlot()
        def on_pushButton_load_clicked(self):
            fileName, _ = QFileDialog.getOpenFileName(self, '打开地址簿', '', '地址簿文件 (*.abk);;所有文件 (*)')
    
            if not fileName:
                return
    
            try:
                in_file = open(str(fileName), 'rb')
            except IOError:
                QMessageBox.information(self, '不能打开文件','打开文件 {} 时发生错误!'.format(fileName))
                return
    
            self.contacts = pickle.load(in_file)
            in_file.close()
    
            if len(self.contacts) == 0:
                QMessageBox.information(self, '文件中无联系人','你打开的文件中无联系人!')
            else:
                for name, address in self.contacts:
                    self.lineEdit_name.setText(name)
                    self.textEdit_address.setText(address)
    
            self.updateInterface(self.NavigationMode)
            
        @pyqtSlot()
        def on_pushButton_save_clicked(self):
            fileName, _ = QFileDialog.getSaveFileName(self, '保存地址簿', '', '地址簿文件 (*.abk);;所有文件 (*)')
    
            if not fileName:
                return
    
            try:
                out_file = open(str(fileName), 'wb')
            except IOError:
                QMessageBox.information(self, '不能打开文件','打开文件 {} 时发生错误!'.format(fileName))
                return
    
            pickle.dump(self.contacts, out_file)
            out_file.close()
            
        @pyqtSlot()
        def on_pushButton_export_clicked(self):
            name = str(self.lineEdit_name.text())
            address = self.textEdit_address.toPlainText()
    
            nameList = name.split()
    
            if len(nameList) > 1:
                firstName = nameList[0]
                lastName = nameList[-1]
            else:
                firstName = name
                lastName = ''
    
            fileName, _ = QFileDialog.getSaveFileName(self, '导出联系', '', 'vCard 文件 (*.vcf);;所有文件 (*)')
    
            if not fileName:
                return
    
            out_file = QFile(fileName)
    
            if not out_file.open(QIODevice.WriteOnly):
                QMessageBox.information(self, '不能打开文件', out_file.errorString())
                return
    
            out_s = QTextStream(out_file)
    
            out_s << 'BEGIN:VCARD' << '
    '
            out_s << 'VERSION:2.1' << '
    '
            out_s << 'N:' << lastName << ';' << firstName << '
    '
            out_s << 'FN:' << ' '.join(nameList) << '
    '
    
            address.replace(';', '\;')
            address.replace('
    ', ';')
            address.replace(',', ' ')
    
            out_s << 'ADR;HOME:;' << address << '
    '
            out_s << 'END:VCARD' << '
    '
    
            QMessageBox.information(self, '导出成功','{} 已经被导出为 vCard !'.format(name))
    
        @pyqtSlot()
        def on_pushButton_previous_clicked(self):
            name = self.lineEdit_name.text()
    
            prev_name = prev_address = None
            for this_name, this_address in self.contacts:
                if this_name == name:
                    break
    
                prev_name = this_name
                prev_address = this_address
            else:
                self.lineEdit_name.clear()
                self.textEdit_address.clear()
                return
    
            if prev_name is None:
                for prev_name, prev_address in self.contacts:
                    pass
    
            self.lineEdit_name.setText(prev_name)
            self.textEdit_address.setText(prev_address)
            
        @pyqtSlot()
        def on_pushButton_next_clicked(self):
            name = self.lineEdit_name.text()
            it = iter(self.contacts)
    
            try:
                while True:
                    this_name, _ = it.next()
    
                    if this_name == name:
                        next_name, next_address = it.next()
                        break
            except StopIteration:
                next_name, next_address = iter(self.contacts).next()
    
            self.lineEdit_name.setText(next_name)
            self.textEdit_address.setText(next_address)
        
        def updateInterface(self, mode):
            self.currentMode = mode
    
            if self.currentMode in (self.AddingMode, self.EditingMode):
                self.lineEdit_name.setReadOnly(False)
                self.lineEdit_name.setFocus(Qt.OtherFocusReason)
                self.textEdit_address.setReadOnly(False)
    
                self.pushButton_add.setEnabled(False)
                self.pushButton_edit.setEnabled(False)
                self.pushButton_remove.setEnabled(False)
    
                self.pushButton_next.setEnabled(False)
                self.pushButton_previous.setEnabled(False)
    
                self.pushButton_submit.show()
                self.pushButton_cancel.show()
    
                self.pushButton_load.setEnabled(False)
                self.pushButton_save.setEnabled(False)
                self.pushButton_export.setEnabled(False)
    
            elif self.currentMode == self.NavigationMode:
                if not self.contacts:
                    self.lineEdit_name.clear()
                    self.textEdit_address.clear()
    
                self.lineEdit_name.setReadOnly(True)
                self.textEdit_address.setReadOnly(True)
                self.pushButton_add.setEnabled(True)
    
                number = len(self.contacts)
                self.pushButton_edit.setEnabled(number >= 1)
                self.pushButton_remove.setEnabled(number >= 1)
                self.pushButton_find.setEnabled(number > 2)
                self.pushButton_next.setEnabled(number > 1)
                self.pushButton_previous.setEnabled(number >1 )
    
                self.pushButton_submit.hide()
                self.pushButton_cancel.hide()
    
                self.pushButton_export.setEnabled(number >= 1)
    
                self.pushButton_load.setEnabled(True)
                self.pushButton_save.setEnabled(number >= 1)
    
    if __name__ == '__main__':
        import sys
        from PyQt5.QtWidgets import QApplication
        
        app = QApplication(sys.argv)
        dlg = Dialog()
        dlg.show()
        sys.exit(app.exec_())
    View Code

    5、程序运行界面

  • 相关阅读:
    php用正则表达式匹配URL的简单方法(亲测可行)
    MySQL中MyISAM与InnoDB区别及选择
    crontab定时任务语法及应用
    “购物狂欢节”如何应对“羊毛党”
    深入理解 Linux 的 RCU 机制
    白夜追凶 :手 Q 图片的显示和发送逻辑
    老司机教你如何优雅地完成一个小项目测试
    腾讯云分布式数据库可用性系统实践
    使用 Skeleton Screen 提升用户感知体验
    实战分享,教你蓝牙在小程序中的应用
  • 原文地址:https://www.cnblogs.com/hhh5460/p/4237766.html
Copyright © 2020-2023  润新知