首页 > 解决方案 > 如何创建带有任意数量按钮的弹出窗口?

问题描述

在 PyQt5 中,我试图创建一个类来实现带有任意数量按钮的弹出窗口。我认为我已经接近了,但是当我显示这个东西时,消息和按钮都不可见。我将不胜感激任何建议。似乎没有其他人尝试过这样做,这有点令人惊讶。Stackoverflow 想要对问题进行更长的描述,但我真的没有什么要说的了。

import os, sys, time
from PyQt5.QtWidgets import QApplication, QDialog, QGridLayout, QPushButton, QTextEdit, \
  QToolButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt


class Popup(QDialog):
   """
   Overview
   --------

   This class implements a substitute for the QMessageBox widget.  `Popup` allows the user to
   define an arbitrary number of buttons with arbitrary names.  The widget can be either modal
   or non-modal.


   Note
   ----

   `Popup` inherits from `QDialog` rather than `QWidget`; otherwise, it would not function as a
   popup widget.
   """


   # Section 11.1: Constructor.

   def __init__(self, *, text="Your message goes here.", button_names=None, icon='warning',
     width=900, height=300, button_style_sheets=None, button_widths=None, parent=None,
     title='', modal=True):
      """
      Inputs
      ------

      `text` is a string containing the text to be displayed.  One can use HTML to control
      fonts, colors, etc.  If one does not specify the font, one gets the default font, which is
      very small.

      `title` is the window title, which is by default an empty string.

      `button_names`, which is `None`, a single string, or a list of strings, optionally names
      the buttons to be displayed below the text.  If there are multiple buttons, these are
      displayed from left to right in the order specified.

      If `button_names` is `None`, the `icon` string determines which buttons are displayed:
      'critical' causes a single 'OK' button to be displayed; anything else causes a pair of
      buttons, with labels 'Yes' and 'No', to be displayed.

      `icon` is the name of the icon to be displayed.  The default is 'warning', which causes
      the code to load the file `warning.ico`.

      `width` and `height` are the width and height of the window in units of pixels.
      """

      super().__init__(parent=parent)

      self.setModal(modal)

      self.setWindowTitle(title)

      self.grid= QGridLayout()
      self.setLayout(self.grid)

      self.textedit= QTextEdit(text)
      self.textedit.setFixedWidth (width)
      self.textedit.setFixedHeight(height)
      self.textedit.setStyleSheet('font-size: 14pt;')

      if button_names is None:

         # Assign default button names:
         if icon.startswith('warning') or icon.startswith('stop'):
            button_names= ['OK']
         else:
            button_names= ['Yes', 'No']

      elif isinstance(button_names, str):
         button_names= [button_names]

      if button_style_sheets is None:
         button_style_sheets= []

         for button_name in button_names:
            if   button_name.lower() in ('y', 'yes'):
               button_style_sheets.append('font-size: 14pt; font-weight: bold; color: #00AA00')
            elif button_name.lower() in ('n', 'no'):
               button_style_sheets.append('font-size: 14pt; font-weight: bold; color: #FF0000')
            else:
               button_style_sheets.append('font-size: 14pt; font-weight: bold')

      if isinstance(button_widths, int):

         # All button widths are the same:
         button_widths= len(button_names) * [button_widths]

      elif button_widths is None:
         button_widths= []

         for button_name in button_names:

            # Set button widths based on the number of characters in the name.
            # The resulting widths are not perfect.
            button_widths.append(20 * (len(button_name) + 2))


      if icon:

         # There is an icon.  ==> The icon occupies the first position in both rows and
         # everything else is pushed one position to the right.

         if icon.endswith('.png'):
            from PIL.ImageQt import ImageQt
            qim= ImageQt(icon)
            pix= QPixmap.fromImage(qim)
            label= QLabel()
            label.setPixmap(pix)
            self.grid.addWidget(label     , 0, 0, 1, 1)
         else:
            self.icon= QToolButton()
            self.icon.setIcon(QIcon(icon))
            self.grid.addWidget(self.icon , 0, 0, 1, 1)

         self.grid.addWidget(self.textedit, 0, 1, 1, len(button_names)+1)

      else:
         self.grid.addWidget(self.textedit, 0, 0, 1, len(button_names)+1)

      self.buttons= []

      for i, button_name in enumerate(button_names):
         button= QPushButton(button_name, parent=self)
         button.setFixedWidth(button_widths[i])

         if isinstance(button_style_sheets, str):
            button.setStyleSheet(button_style_sheets)
         else:
            button.setStyleSheet(button_style_sheets[i])

         self.buttons.append(button)

         self.grid.addWidget(button, 1, i+bool(icon), 1, 1, alignment=Qt.AlignRight)

         def make_func(button_name):
            def set_result():
               self.result= button_name
            return set_result

         def close():
            self.close()

         button.clicked.connect(make_func(button_name))
         button.clicked.connect(close)

      # end for i, button_name in button_names

   # end def __init__


app = QApplication(sys.argv)
popup= Popup(text="Now is the time for all good men to come to the aid of their country.",
  button_names=['Yes', 'No', 'Maybe'])
popup.show()
time.sleep(3)

标签: pythonpyqt5popup

解决方案


您不应该在 Qt 中使用 time.sleep(),如果您希望程序显示一段时间,请使用调用 quit() 方法的 QTimer。

QTimer.singleShot(3 * 1000, QCoreApplication.quit)
sys.exit(app.exec_())

如果您只想关闭弹出窗口而不是所有其他窗口,请将其连接到 close() 方法:

QTimer.singleShot(3 * 1000, popup.close)
sys.exit(app.exec_())

推荐阅读