人间倦游者 發表於 2025-12-21 14:14:00

在PySide6/PyQt6的项目中封装一些基础类库,包括文件对话框、字体对话框、颜色对话框、消息对话框等内容

<p>在我们实际开发项目的时候,有时候为了使用方便,会针对一些常用到的内容进行一定的封装处理,以降低使用的难度和减少相关代码,本篇随笔介绍在PySide6/PyQt6的项目中封装一些基础类库,包括文件对话框、字体对话框、颜色对话框、消息对话框等内容。</p>
<h3>1、常用对话框处理封装的优点</h3>
<p>对常用对话框的调用(包括文件对话框、字体对话框、颜色对话框、消息对话框等内容),可能调用的时候,会遇到一些问题,如对于常用的文件目录对话框,可能会出现下面一些问题:</p>
<ul>
<li data-start="332" data-end="340">
<p data-start="334" data-end="340">参数顺序难记</p>
</li>
<li data-start="341" data-end="368">
<p data-start="343" data-end="368">单选 / 多选 / 保存 / 目录 API 不统一</p>
</li>
<li data-start="369" data-end="379">
<p data-start="371" data-end="379">最近路径不好维护</p>
</li>
<li data-start="380" data-end="390">
<p data-start="382" data-end="390">每个窗口都写一遍</p>
</li>
</ul>
<p>如果对它进行一定的封装,可以实现更多可选参数的设置以及更好的支持:</p>
<p>✅ 打开单文件<br data-start="871" data-end="874">
✅ 打开多文件<br data-start="881" data-end="884">
✅ 保存文件<br data-start="890" data-end="893">
✅ 选择目录<br data-start="899" data-end="902">
✅ 文件过滤器常量<br data-start="911" data-end="914">
✅ 最近目录记忆(进程级 / 可扩展到配置)<br data-start="936" data-end="939">
✅ Windows / macOS / Linux 兼容</p>
<p>如果对这些对话框进行辅助类的统一封装,会具有以下是一些主要的优点:</p>
<p>1)<strong>代码复用</strong></p>
<p>封装常用对话框可以避免重复代码。你可以定义一个统一的函数或类来处理所有常用对话框操作,从而在多个地方复用这段代码。</p>
<p>2)<strong>一致性</strong></p>
<p>通过封装,你可以确保所有常用对话框的外观和行为一致。这有助于提高用户体验,使用户在应用程序中获得统一的交互方式。</p>
<p>3)&nbsp;<strong>简化调用</strong></p>
<p>封装可以简化调用过程。你可以将常用的参数设置(如标题、图标、按钮类型等)预先定义好,从而在调用时减少参数输入。</p>
<p>4)<strong>易于维护</strong></p>
<p>当需要更改对话框的行为或样式时,只需在封装函数中进行修改,而不必在应用程序中的每个调用点进行更改。这使得维护变得更加简单和高效。</p>
<p>5)<strong>增强可读性</strong></p>
<p>通过使用封装的函数或类,代码变得更易读。其他开发者可以一眼看出对话框的作用,而不必深入了解其具体实现。</p>
<p>6)<strong>集中管理</strong></p>
<p>封装有助于集中管理对话框的逻辑,比如处理用户输入、响应用户选择等。这样可以更方便地进行逻辑更新或错误处理。</p>
<p>7)&nbsp;<strong>扩展性</strong></p>
<p>如果将来需要增加新的对话框或修改现有对话框的逻辑,封装使得扩展更加容易。你可以在封装的基础上进行扩展,而不影响现有的代码结构。</p>
<h2>2、文件对话框的封装</h2>
<p>如果我们对文件对话框进行封装,那么需要考虑打开、保存文件对话框等常规操作,而文件的格式有多种多样,我们为了方便,可以提供更细节的函数选择具体的文件,如文本文件、Excel文件等。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)"><strong> FileDialogUtil</strong></span>:
    </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">文件、目录对话框工具类</span><span style="color: rgba(128, 0, 0, 1)">"""</span>

    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 定义文件过滤器</span>
    all_filter = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">All File (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    word_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Word (*.doc);;*.doc;;Word (*.docx);;*.docx;;All File (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    excel_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Excel (*.xls);;*.xls;;Excel (*.xlsx);;*.xlsx;;All File (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    pdf_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PDF (*.pdf);;*.pdf;;All File (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    image_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Image Files (*.BMP;*.bmp;*.JPG;*.jpg;*.GIF;*.gif;*.png;*.PNG);;*.BMP;*.bmp;*.JPG;*.jpg;*.GIF;*.gif;*.png;*.PNG;;All File (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    html_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">HTML files (*.html;*.htm);;*.html;*.htm;;All files (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    access_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Access (*.mdb);;*.mdb;;All File (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    zip_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Zip (*.zip);;*.zip;;Rar (*.rar);;*.rar;;All files (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    config_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Configuration Files (*.cfg);;*.cfg;;All File (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    txt_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Text (*.txt);;*.txt;;All files (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    xml_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">XML Files (*.xml);;*.xml;;All files (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    rar_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Rar (*.rar);;*.rar;;All files (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    sqlite_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Sqlite Files (*.db);;*.db;;All files (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    python_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Python Files (*.py);;*.py;;All files (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    csv_filter </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CSV Files (*.csv);;*.csv;;All files (*.*);;*.*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">

    @staticmethod
    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> open_file(
      parent: QWidget </span>=<span style="color: rgba(0, 0, 0, 1)"> None,
      multiple: bool </span>=<span style="color: rgba(0, 0, 0, 1)"> False,
      title: str </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">打开文件</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
      filter: str </span>=<span style="color: rgba(0, 0, 0, 1)"> all_filter,
      filename: str </span>= <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
      initial_directory: str </span>=<span style="color: rgba(0, 0, 0, 1)"> os.getcwd(),
    ) </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> str:
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
      打开文件对话框

      :param parent: 父窗口
      :param multiple: 是否多选
      :param title: 对话框标题
      :param filename: 默认文件名
      :param filter: 文件过滤器
      :param initial_directory: 默认目录
      :return: 选中的文件路径,如果是多选,则返回以逗号分隔的多个文件路径
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span>

      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建文件对话框</span>
      dialog =<span style="color: rgba(0, 0, 0, 1)"> QFileDialog(parent)
      dialog.setWindowTitle(title)
      dialog.setFileMode(
            QFileDialog.FileMode.ExistingFiles
            </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> multiple
            </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> QFileDialog.FileMode.ExistingFile
      )
      dialog.setNameFilter(filter)
      dialog.setDirectory(initial_directory)
      dialog.selectFile(filename)

      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 执行对话框并获取文件路径</span>
      result = <span style="color: rgba(128, 0, 0, 1)">""</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> dialog.<span style="color: rgba(0, 0, 255, 1)">exec</span><span style="color: rgba(0, 0, 0, 1)">():
            </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> multiple:
                file_paths </span>=<span style="color: rgba(0, 0, 0, 1)"> dialog.selectedFiles()
                result </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">,</span><span style="color: rgba(128, 0, 0, 1)">"</span>.join(file_paths)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 将文件路径连接成一个字符串</span>
            <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">:
                result </span>= dialog.selectedFiles()<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获取单个文件路径</span>

      <span style="color: rgba(0, 0, 255, 1)">return</span> result</pre>
</div>
<p>当然上面&nbsp;<span style="font-size: 15px">open_file&nbsp;</span>传入的是所有文件的格式,如果我们需要跟进一步选择Excel格式,就需要传入对应的后缀名参数常量即可。如下所示。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202512/8867-20251219162242077-772752499.png" alt="image" width="749" height="852" loading="lazy"></p>
<p>&nbsp;而如果要弹出保存文件对话框的操作,那么我们也是如法炮制即可。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @staticmethod
    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> save_file(
      parent: QWidget </span>=<span style="color: rgba(0, 0, 0, 1)"> None,
      title: str </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">保存文件</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
      filter: str </span>=<span style="color: rgba(0, 0, 0, 1)"> all_filter,
      filename: str </span>= <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
      initial_directory: str </span>=<span style="color: rgba(0, 0, 0, 1)"> os.getcwd(),
    ) </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> str:
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">以指定的标题弹出保存文件对话框

      :param title: 对话框标题
      :param filename: 默认文件名
      :param filter: 文件过滤器
      :param initial_directory: 默认目录
      :return: 选中的文件路径
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span>
      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建保存文件对话框</span>
      dialog =<span style="color: rgba(0, 0, 0, 1)"> QFileDialog(parent)
      dialog.setWindowTitle(title)
      dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置为保存模式</span>
<span style="color: rgba(0, 0, 0, 1)">      dialog.setNameFilter(filter)
      dialog.setDirectory(initial_directory)
      dialog.selectFile(filename)

      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 执行对话框并获取文件路径</span>
      result = <span style="color: rgba(128, 0, 0, 1)">""</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> dialog.<span style="color: rgba(0, 0, 255, 1)">exec</span><span style="color: rgba(0, 0, 0, 1)">():
            result </span>= dialog.selectedFiles()<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获取选中的文件路径</span>

      <span style="color: rgba(0, 0, 255, 1)">return</span> result</pre>
</div>
<p>其他类型格式的,只需要传入对应的filter格式即可。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202512/8867-20251219162506602-1495295598.png" alt="image" width="740" height="741" loading="lazy"></p>
<p>&nbsp;选择目录也是类似的处理</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @staticmethod
    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> open_dir(
      parent: QWidget </span>=<span style="color: rgba(0, 0, 0, 1)"> None,
      title: str </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">选择目录</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
      initial_directory: str </span>=<span style="color: rgba(0, 0, 0, 1)"> os.getcwd(),
    ) </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> str:
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">显示目录选择对话框</span><span style="color: rgba(128, 0, 0, 1)">"""</span>

      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建文件对话框</span>
      dialog =<span style="color: rgba(0, 0, 0, 1)"> QFileDialog(parent)
      dialog.setWindowTitle(title)
      dialog.setFileMode(QFileDialog.FileMode.Directory)</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置为目录选择模式</span>
      dialog.setOption(QFileDialog.Option.ShowDirsOnly, True)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 只显示目录</span>
<span style="color: rgba(0, 0, 0, 1)">      dialog.setDirectory(initial_directory)

      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 执行对话框并获取选中的目录</span>
      result = <span style="color: rgba(128, 0, 0, 1)">""</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> dialog.<span style="color: rgba(0, 0, 255, 1)">exec</span><span style="color: rgba(0, 0, 0, 1)">():
            result </span>= dialog.selectedFiles()<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获取选中的目录路径</span>

      <span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(result)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> result</pre>
</div>
<p>选择多个目录,如下效果</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202512/8867-20251219163413565-205932733.png" alt="image" width="640" height="326" loading="lazy"></p>
<p>这样我们在一些窗体上使用保存Excel或者PDF文件的时候,直接使用它的函数调用即可,比较简单了。</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">def</span> export_to_pdf(self, setting: PrintSetting) -&gt;<span style="color: rgba(0, 0, 0, 1)"> str:
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">将 QTableView 的数据导出为 PDF, 成功返回文件路径,失败返回空字符串</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">

      pdf_file </span>= <span style="color: rgba(255, 0, 0, 1)"><strong>FileDialogUtil.save_pdf</strong></span>(filename=f<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{setting.print_title}.pdf</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span><span style="color: rgba(0, 0, 0, 1)"> pdf_file:
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">""</span>

      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建 QPrinter,PDF格式</span>
      printer =<span style="color: rgba(0, 0, 0, 1)"> QPrinter(QPrinter.PrinterMode.HighResolution)
      printer.setOutputFormat(QPrinter.OutputFormat.PdfFormat)
      printer.setOutputFileName(pdf_file)</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 输出 PDF 文件</span>

      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 打印的处理</span>
<span style="color: rgba(0, 0, 0, 1)">      printer.setPageSize(setting.page_size)
      printer.setPageOrientation(setting.page_orientation)
      self.print_cols </span>= setting.print_cols<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 打印指定列的索引列表</span>
      self.print_title = setting.print_title<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 打印标题</span>
      self.settings = setting<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 保存打印设置</span>

      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 打印输出</span>
<span style="color: rgba(0, 0, 0, 1)">      self.print_preview_paint(printer)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> pdf_file</pre>
</div>
<p>&nbsp;</p>
<h3>3、封装常用消息对话框</h3>
<p>封装的消息提示对话框包括个各种常用的对话框,如下所示:<br><img src="https://images.cnblogs.com/cnblogs_com/wuhuacong/Commons/MessageUtil.png" alt="" width="650" height="950" class="medium-zoom-image"></p>
<p>首先我们需要定义一个独立的消息对话框类MessageUtil,如下所示。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MessageUtil:
    </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
    封装了常用的消息对话框,以方便使用常用对话框消息。
    包括提示信息、警告信息、错误信息、确认信息、询问信息、输入信息、
    选择信息、多选信息、文件选择信息、目录选择信息、字体选择信息、颜色选择信息、进度条信息等。
    </span><span style="color: rgba(128, 0, 0, 1)">"""</span>

    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 常用消息对话框的标题</span>
    CAPTION_TIPS = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">提示信息</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    CAPTION_WARNING </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">警告信息</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    CAPTION_ERROR </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">错误信息</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    CAPTION_CONFIRM </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">确认信息</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">

   @staticmethod
    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> show_message(
      message: str,
      title: str </span>=<span style="color: rgba(0, 0, 0, 1)"> CAPTION_TIPS,
      extended_message</span>=<span style="color: rgba(0, 0, 0, 1)">None,
      parent</span>=<span style="color: rgba(0, 0, 0, 1)">None,
      icon: QMessageBox.Icon </span>=<span style="color: rgba(0, 0, 0, 1)"> QMessageBox.Icon.Information,
      buttons: QMessageBox.StandardButton </span>=<span style="color: rgba(0, 0, 0, 1)"> QMessageBox.StandardButton.Ok,
    ) </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> QMessageBox.StandardButton:
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
      通用消息框显示函数
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
      msg_box </span>=<span style="color: rgba(0, 0, 0, 1)"> QMessageBox(parent)
      msg_box.setWindowTitle(title)
      msg_box.setText(message)

      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置详细信息,在消息框的底部显示。</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> extended_message:
            </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建 TextSplitter 实例,设置每行最大字符数为 30</span>
            splitter = TextSplitter(max_line_length=80<span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 使用 splitter 来分割文本</span>
            extended_message =<span style="color: rgba(0, 0, 0, 1)"> splitter.split(extended_message)
            msg_box.setDetailedText(extended_message)

      msg_box.setDetailedText(extended_message)
      msg_box.setIcon(icon)
      msg_box.setStandardButtons(buttons)
      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置窗口图标</span>
      app_icon = QIcon(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">app/images/app.ico</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
      msg_box.setWindowIcon(app_icon)</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> msg_box.<span style="color: rgba(0, 0, 255, 1)">exec</span>()</pre>
</div>
<p>然后统一对常用的消息进行函数封装,如一般消息、警告消息、错误消息,提示消息等,只需要对上面函数的简单调用,传递不同的参数就可以。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202512/8867-20251219163016118-169649803.png" alt="image" width="839" height="832" loading="lazy"></p>
<p>有了这样的封装,我们可以在窗体中直接调用,不需要记住太多的参数了,比较简单,如下面的删除操作处理。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @asyncSlot()
    async </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> OnDelete(self):
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">弹出删除对话框</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
      selected_rows </span>=<span style="color: rgba(0, 0, 0, 1)"> self.table_view.selectionModel().selectedRows()
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">not</span><span style="color: rgba(0, 0, 0, 1)"> selected_rows:
            <strong><span style="color: rgba(255, 0, 0, 1)">MessageUtil.show_info</span></strong>(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">请选择要操作的行</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 0, 255, 1)">return</span>
      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 确认删除</span>
      result = <span style="color: rgba(255, 0, 0, 1)"><strong>MessageUtil.show_confirm</strong></span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">确认删除选中的行?</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">确认删除</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> result:
            </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 遍历选定的行,删除主键值对应的记录</span>
            list =<span style="color: rgba(0, 0, 0, 1)"> []
            </span><span style="color: rgba(0, 0, 255, 1)">for</span> index <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> selected_rows:
                entity_id </span>=<span style="color: rgba(0, 0, 0, 1)"> self.table_model.GetPrimaryKeyValue(index.row())
                </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> entity_id:
                  list.append(entity_id)</span>
            <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
                await self.OnDeleteByIdList(list)
            </span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> Exception as e:
                <span style="color: rgba(255, 0, 0, 1)"><strong>MessageUtil.show_error</strong></span>(f</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">删除失败:{e}</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<p>&nbsp;为了了解常用对话框的操作,我们还编写一个简单的测试界面来展示效果。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202512/8867-20251219163228916-401571327.png" alt="image" width="1079" height="671" loading="lazy"></p>
<p>&nbsp;</p>
<p><strong>字体对话框</strong></p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202512/8867-20251219163304562-551122377.png" alt="image" width="658" height="514" loading="lazy"></p>
<p>封装字体对话框函数如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @staticmethod
    </span><span style="color: rgba(0, 0, 255, 1)">def</span> show_font_dialog(parent=None, font=None) -&gt;<span style="color: rgba(0, 0, 0, 1)"> tuple:
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
      显示字体选择对话框
      :param parent: 父窗口
      :param font: 默认字体,如果没有提供,使用系统默认字体
      :return: 选择的字体,成功时返回 (QFont, True),失败时返回 (None, False)
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span>
      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果没有提供默认字体,则使用系统默认字体</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> font <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> None:
            font </span>= QApplication.font()<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获取系统默认字体</span>

      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 打开字体选择对话框</span>
      ok, selected_font =<span style="color: rgba(0, 0, 0, 1)"> QFontDialog.getFont(font, parent)

      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 返回选择的字体和是否成功</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> selected_font, ok</pre>
</div>
<p>调用代码如下所示。</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> on_select_font(self):
      myfont </span>= self.message.font()<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 获取当前字体</span><span style="color: rgba(0, 0, 0, 1)">
      font_data </span>= MessageUtil.show_font_dialog(self, myfont)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 打开字体选择对话框</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> font_data:
            self.message.setFont(
                QFont(font_data.family(), font_data.pointSize())
            )</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 设置字体</span>
            self.message.update()<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 刷新显示</span></pre>
</div>
<p>&nbsp;</p>
<p><strong>颜色对话框</strong></p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202512/8867-20251219163326668-614177190.png" alt="image" width="661" height="528" loading="lazy"></p>
<p>封装颜色对话框函数如下:</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">def</span> show_colour_dialog(parent=None, colour=None) -&gt;<span style="color: rgba(0, 0, 0, 1)"> tuple:
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
      显示颜色选择对话框
      :param parent: 父窗口
      :param colour: 默认颜色,如果没有提供,使用黑色
      :return: 选择的颜色,成功时返回 (QColor, True),失败时返回 (None, False)
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span>
      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 如果没有提供默认颜色,则使用黑色</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> colour <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> None:
            colour </span>=<span style="color: rgba(0, 0, 0, 1)"> QColor(Qt.GlobalColor.black)

      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 打开颜色选择对话框</span>
      selected_colour =<span style="color: rgba(0, 0, 0, 1)"> QColorDialog.getColor(colour, parent)

      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 返回选择的颜色和是否成功</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span> selected_colour, selected_colour.isValid()</pre>
</div>
<p>调用代码如下所示。</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> on_select_colour(self):</span><span style="color: rgba(0, 0, 0, 1)">
      color </span>=<span style="color: rgba(0, 0, 0, 1)"> self.message.palette().color(QPalette.ColorRole.WindowText)
      color_data </span>=<span style="color: rgba(0, 0, 0, 1)"> MessageUtil.show_colour_dialog(self, color)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> color_data:
            self.message.setStyleSheet(f</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">color: {color_data.name()};</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            self.message.update()</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 刷新显示</span></pre>
</div>
<p>通过上面的简单封装,我们就可以很容易的记得相关的处理函数,并且尽可能的减少了相关的参数传递,这样我们在使用的时候,更加方便灵活了。</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="border-right-color: #cccccc; border-right-width: 1px; border-right-style: solid; padding-right: 5px; border-top-color: #cccccc; border-top-width: 1px; border-top-style: solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left-color: #cccccc; border-left-width: 1px; border-left-style: solid; width: 98%; padding-top: 4px; border-bottom-color: #cccccc; border-bottom-width: 1px; border-bottom-style: solid; background-color: #eeeeee;">
    <img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt>
    <span style="color: #000000"><span class="Apple-tab-span" style="white-space: pre"></span>
   专注于代码生成工具、.Net/Python 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架、Python开发框架等框架产品。
   <br>  转载请注明出处:撰写人:伍华聪  http://www.iqidi.com <br>    </span></div><br><br>
来源:https://www.cnblogs.com/wuhuacong/p/19372630
頁: [1]
查看完整版本: 在PySide6/PyQt6的项目中封装一些基础类库,包括文件对话框、字体对话框、颜色对话框、消息对话框等内容