Bootstrap

pyqt -- 实现Toast/多页面跳转/弹框

# PyQt5==5.12
from PyQt5.QtCore import Qt, QPropertyAnimation, QSize, QTimer
from PyQt5.QtGui import QIcon, QPixmap, QPainter, QPaintEvent, QBrush
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QStackedWidget, QDesktopWidget, QHBoxLayout, QWidget, QLabel, \
    QSizePolicy, QFileDialog
    
class Toast(QWidget):
    style = """#LabelMessage{color:white;font-family:Microsoft YaHei;}"""

    def __init__(self, message='', timeout=2000, parent=None):
        """
        @param message: 提示信息
        @param timeout: 窗口显示时长
        @param parent: 父窗口控件
        """
        super(Toast, self).__init__()
        self.parent = parent
        # 除了成功之外其他消息图标一律使用错误图标(有局限性:成功的具体信息有很多种,但这里没办法区分,待改)
        self.timer = QTimer()
        # 由于不知道动画结束的事件,所以借助QTimer来关闭窗口,动画结束就关闭窗口,所以这里的事件要和动画时间一样
        self.timer.singleShot(timeout, self.close)  # singleShot表示timer只会启动一次

        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.SubWindow)
        # self.setWindowOpacity(0.9)  # 设置窗口透明度
        self.setAttribute(Qt.WA_TranslucentBackground)  # 设置窗口透明
        self.setObjectName('Toast')
        # self.setFixedSize(QSize(220, 100))
        self.setMinimumSize(QSize(220, 100))
        self.setMaximumSize(QSize(220, 180))

        layout = QHBoxLayout()
        layout.setContentsMargins(20, -1, 20, -1)
        layout.setObjectName("HorizontalLayout")
        self.setLayout(layout)

        self.initUi(layout, message)
        self.createAnimation(timeout)

        self.setStyleSheet(Toast.style)
        self.center()

    def initUi(self, layout, message):
        messageLabel = QLabel()
        # 实现QLabel自动换行
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(messageLabel.sizePolicy().hasHeightForWidth())
        messageLabel.setSizePolicy(sizePolicy)
        messageLabel.setWordWrap(True)
        messageLabel.setText(message)
        messageLabel.setTextFormat(Qt.AutoText)
        messageLabel.setScaledContents(True)
        messageLabel.setObjectName("LabelMessage")
        messageLabel.setAlignment(Qt.AlignCenter)
        layout.addWidget(messageLabel)

    def paintEvent(self, a0: QPaintEvent):
        qp = QPainter()
        qp.begin(self)  # 不能掉,不然没效果
        qp.setRenderHints(QPainter.Antialiasing, True)  # 抗锯齿
        qp.setBrush(QBrush(Qt.black))
        qp.setPen(Qt.transparent)
        rect = self.rect()
        rect.setWidth(rect.width() - 1)
        rect.setHeight(rect.height() - 1)
        qp.drawRoundedRect(rect, 15, 15)
        qp.end()

    def createAnimation(self, timeout):
        # 1.定义一个动画
        self.animation = QPropertyAnimation(self, b'windowOpacity')
        self.animation.setTargetObject(self)
        # 2.设置属性值
        self.animation.setStartValue(0)
        self.animation.setKeyValueAt(0.2, 0.7)  # 设置插值0.3 表示单本次动画时间的0.3处的时间点
        self.animation.setKeyValueAt(0.8, 0.7)  # 设置插值0.8 表示单本次动画时间的0.3处的时间点
        self.animation.setEndValue(0)
        # 3.设置时长
        self.animation.setDuration(timeout)
        # 4.启动动画
        self.animation.start()

    def center(self):
        if self.parent is not None:
            xPos = self.parent.x() + int((self.parent.width() - self.width()) / 2)
            yPos = self.parent.y() + int((self.parent.height() - self.height()) / 2 + 40)
            self.move(xPos, yPos)
        else:
            # 屏幕居中
            screen = QDesktopWidget().screenGeometry()
            size = self.geometry()
            self.move(int((screen.width() - size.width()) / 2),
                      int((screen.height() - size.height()) / 2) + 40)

class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.setWindowTitle("分析工具")  # 设置窗口标题
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowMaximizeButtonHint)  # 禁止最大化按钮
        self.setWindowIcon(QIcon(LOGO_PATH))  # 设置窗口图标,icon.png 是您的图标文件路径
        self.stacked_widget = QStackedWidget()  # 创建堆叠窗口
        self.setCentralWidget(self.stacked_widget)

        self.shouye = self.load_ui(SHOUYE_UI)  # 绑定首页UI
        self.fenxizhong = self.load_ui(FENXIZHONG_UI)  # 分析中UI
        self.shenqingqushi = self.load_ui(SHENQINGQUSHI_UI)  # 申请趋势
        self.bujudiyu = self.load_ui(BUJUDIYU_UI)  # 布局地域分析
        self.bujudiyu_shijiantaishi = self.load_ui(BUJUDIYU_SHIJIANTAISHI_UI)  # 布局地域时间态势分析
        self.jishulingyu_zhanbi = self.load_ui(JISHULINGYU_ZHANBI_UI)  # 技术领域占比分析
        self.jishulingyu_shijiantaishi = self.load_ui(JISHULINGYU_SHIJIANTAISHI_UI)  # 技术领域时间态势分析
        self.famingrenguanxi = self.load_ui(FAMINGRENGUANXI_UI)  # 发明人关系
        self.jishulingyu = self.load_ui(JISHULINGYU_UI)  # 技术领域
        self.switch_to_shouye()  # 初始化页面到首页
        self.binding_redirect()

        self.xlsx_path = None  # 选择xlsx文件绝对路径

    def showToast(self, message):
        Toast(message, parent=self).show()

    def open_file_dialog(self):
        '''xlsx文件选择器'''
        file_path, _ = QFileDialog.getOpenFileName(self, '选择文件', '', 'Excel Files (*.xlsx)',
                                                   options=QFileDialog.Options())
        if file_path:
            logger.debug(f'excel文件选择为:【{file_path}】')
            self.xlsx_path = file_path
            self.shouye.label_6.setText(f'文件:{file_path}')

    def switch_to_shouye(self):
        '''切换首页'''
        self.stacked_widget.setCurrentWidget(self.shouye)
        self.shouye.label_2.setPixmap(QPixmap(UPLOAD_PATH))  # 加载上传的云朵图标
        self.shouye.lineEdit_6.setFocus()  # 给公司信息输入框获取焦点
        self.shouye.pushButton_17.clicked.connect(self.open_file_dialog)  # 选择文件

    def switch_to_fenxizhong(self):
        '''切换分析中'''
        self.stacked_widget.setCurrentWidget(self.fenxizhong)
        _async(target=self.progress_bar).start()

    def switch_to_shenqingqushi(self):
        '''切换申请趋势'''
        self.stacked_widget.setCurrentWidget(self.shenqingqushi)

    def switch_to_bujudiyu(self):
        '''切换布局地域分析'''
        self.stacked_widget.setCurrentWidget(self.bujudiyu)

    def switch_to_bujudiyu_shijiantaishi(self):
        '''切换布局地域时间态势分析'''
        self.stacked_widget.setCurrentWidget(self.bujudiyu_shijiantaishi)

    def switch_to_jishulingyu_zhanbi(self):
        '''切换技术领域占比分析'''
        self.stacked_widget.setCurrentWidget(self.jishulingyu_zhanbi)

    def switch_to_jishulingyu_shijiantaishi(self):
        '''切换技术领域时间态势分析'''
        self.stacked_widget.setCurrentWidget(self.jishulingyu_shijiantaishi)

    def switch_to_famingrenguanxi(self):
        '''切换发明人关系'''
        self.stacked_widget.setCurrentWidget(self.famingrenguanxi)

    def swich_to_jishulingyu(self):
        '''切换技术领域'''
        self.stacked_widget.setCurrentWidget(self.jishulingyu)

    def progress_bar(self):
        '''进度条'''
        for i in range(1, 101):
            self.fenxizhong.progressBar.setValue(i)
            time.sleep(0.01)

        time.sleep(0.5)
        self.switch_to_shenqingqushi()

    def binding_redirect(self):
        '''绑定跳转关系'''
        menu_url = [self.shenqingqushi, self.bujudiyu, self.bujudiyu_shijiantaishi, self.jishulingyu_zhanbi,
                    self.jishulingyu_shijiantaishi, self.famingrenguanxi, self.jishulingyu]
        menu_button = [f'pushButton_{num}' for num in range(21, 28)]
        url_func = [self.switch_to_shenqingqushi, self.switch_to_bujudiyu, self.switch_to_bujudiyu_shijiantaishi,
                    self.switch_to_jishulingyu_zhanbi, self.switch_to_jishulingyu_shijiantaishi,
                    self.switch_to_famingrenguanxi, self.swich_to_jishulingyu]

        for url in menu_url:
            for index, menu in enumerate(menu_button):
                getattr(url, menu).clicked.connect(url_func[index])

    
    def closeEvent(self, event):
        '''窗口被关闭事件'''
        msgBox = CustomMessageBox()
        msgBox.setWindowTitle('分析工具')
        msgBox.setText('您确认关闭系统吗?')
        msgBox.addButton(QMessageBox.Yes)
        msgBox.addButton(QMessageBox.No)
        yes_button = msgBox.button(QMessageBox.Yes)
        yes_button.setText('是')
        no_button = msgBox.button(QMessageBox.No)
        no_button.setText('否')
        reply = msgBox.exec_()  # 显示消息框,并等待用户的响应
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()  # 忽略关闭事件


    def load_ui(self, path):
        '''加载UI'''
        ui = loadUi(path)
        self.stacked_widget.addWidget(ui)
        return ui

class LogicViews(MyWindow):
    '''逻辑执行'''

    def __init__(self):
        super().__init__()
        self.shouye.pushButton_18.clicked.connect(self.shouye_views)  # 点击首页开始分析


    def shouye_views(self):
        '''首页逻辑'''
        corporate_name = self.shouye.lineEdit_6.text()
        if corporate_name in ('', None):
            return self.showToast('公司名称不可为空')

        elif self.xlsx_path is None:
            return self.showToast('请先上传文件')

        self.switch_to_fenxizhong()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = LogicViews()
    window.showMaximized()
    window.show()
    sys.exit(app.exec_())
;