Skip to content

fix: defer printer initialization until ready - #794

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
wjyrich:fix-bug-375053
Oct 28, 2026
Merged

deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
wjyrich:fix-bug-375053

Conversation

@wjyrich

@wjyrich wjyrich commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor
  1. Move heavy printer initialization (DPrinter construction, default printer query) into the async printer info fetch instead of the dialog constructor
  2. Add copyPrintSetting helper to deep-copy DPrintPreviewSettingInfo objects, preventing use-after-free of caller-owned settings that may be destroyed before startup completes
  3. Queue pending settings, doc name, and print path when the dialog is not yet initialized and apply them after printer info arrives
  4. Add isInited guards in event, eventFilter, and setPrintFromPath to avoid touching uninitialized UI during loading
  5. Disable scroll area, preview widget, and print button during loading; keep cancel button usable so users can dismiss immediately
  6. Use QSharedPointer ownership to safely discard in-flight results when the dialog closes during construction
  7. Pre-initialize QPlatformPrinterSupportPlugin before querying from worker threads to avoid unsynchronized plugin access
  8. Add DefaultPrinterInfo struct to carry both printer name and constructed DPrinter from the worker
  9. Re-enable and re-enable the disabled unit tests for ut_dprintpreviewdialog with comprehensive coverage of loading state, pending settings, async preview, PDF export, and cancel/reopen scenarios

Log: The print preview dialog now opens instantly and validates printer availability in the background

Influence:

  1. Open the print preview dialog and verify it appears immediately without freezing
  2. Confirm the cancel button works while printer info is still loading
  3. Test that setDocName, setPrintFromPath, and updateDialogSettingInfo called before show are preserved and applied after startup
  4. Verify async preview (setAsynPreview), n-up imposition, and page range settings work when applied before startup
  5. Test that closing the dialog during loading does not crash or hang
  6. Verify reopening the dialog after cancel reuses the printer correctly
  7. Test PDF export after the dialog has fully initialized
  8. Verify that installed settings plugins receive pending setting info in correct order

fix: 延迟打印机初始化直至就绪

  1. 将耗时的打印机初始化(DPrinter 构造、默认打印机查询)从对话框构造函数 移入异步打印机信息获取流程
  2. 添加 copyPrintSetting 辅助函数深拷贝 DPrintPreviewSettingInfo 对象, 避免调用方在启动完成前销毁设置对象导致的悬空引用
  3. 对话框未初始化时缓存待处理的设置、文档名和打印路径,待打印机信息就绪 后统一应用
  4. 在 event、eventFilter 和 setPrintFromPath 中添加 isInited 判断,避免 加载期间操作未初始化的 UI
  5. 加载期间禁用滚动区域、预览控件和打印按钮,保持取消按钮可用以便用户随 时关闭
  6. 使用 QSharedPointer 管理所有权,安全释放对话框在构造过程中 关闭时被丢弃的结果
  7. 在工作线程查询前预初始化 QPlatformPrinterSupportPlugin,避免插件的非 同步访问
  8. 新增 DefaultPrinterInfo 结构,从工作线程同时返回打印机名称和已构造 的 DPrinter
  9. 重新启用 ut_dprintpreviewdialog 单元测试,补充加载状态、待处理设置、 异步预览、PDF 导出及取消重开等场景覆盖

Log: 打印预览对话框现可即时打开并在后台校验打印机可用性

Influence:

  1. 打开打印预览对话框,确认立即可见且无卡顿
  2. 确认在打印机信息加载期间取消按钮可正常使用
  3. 测试在 show 之前调用 setDocName、setPrintFromPath 和 updateDialogSettingInfo 时,相关设置被保留并在启动后生效
  4. 验证在启动前设置的异步预览(setAsynPreview)、N 合一拼版和页码范围设 置均能正确生效
  5. 测试加载期间关闭对话框不会崩溃或卡死
  6. 验证取消后重新打开对话框能正确复用打印机
  7. 测试对话框完全初始化后的 PDF 导出功能
  8. 验证已安装的设置插件按正确顺序收到待处理设置信息

PMS:BUG-375053

Summary by Sourcery

Make the print preview dialog initialize asynchronously while preserving pre-startup state and remaining safe to cancel or close during loading.

Bug Fixes:

  • Defer printer construction and availability checks to asynchronous startup so the print preview dialog opens without blocking.
  • Preserve document names, print paths, and settings supplied before initialization, including safe copying of caller-owned setting data.
  • Prevent crashes and hangs when interacting with or closing the dialog while printer information is loading, including safe reuse after reopening.

Enhancements:

  • Disable print controls while loading while keeping cancellation available, then apply pending state and refresh the preview once initialization completes.
  • Use managed printer ownership and initialize platform printer support safely for background queries.

Tests:

  • Re-enable and expand print preview dialog tests covering responsive loading, pending state, asynchronous previews, page ranges, n-up printing, PDF export, cancellation, destruction, and reopening.

1. Move heavy printer initialization (DPrinter construction, default
printer query) into the async printer info fetch instead of the dialog
constructor
2. Add copyPrintSetting helper to deep-copy DPrintPreviewSettingInfo
objects, preventing use-after-free of caller-owned settings that may be
destroyed before startup completes
3. Queue pending settings, doc name, and print path when the dialog is
not yet initialized and apply them after printer info arrives
4. Add isInited guards in event, eventFilter, and setPrintFromPath to
avoid touching uninitialized UI during loading
5. Disable scroll area, preview widget, and print button during loading;
keep cancel button usable so users can dismiss immediately
6. Use QSharedPointer<DPrinter> ownership to safely discard in-flight
results when the dialog closes during construction
7. Pre-initialize QPlatformPrinterSupportPlugin before querying from
worker threads to avoid unsynchronized plugin access
8. Add DefaultPrinterInfo struct to carry both printer name and
constructed DPrinter from the worker
9. Re-enable and re-enable the disabled unit tests for
ut_dprintpreviewdialog with comprehensive coverage of loading state,
pending settings, async preview, PDF export, and cancel/reopen scenarios

Log: The print preview dialog now opens instantly and validates printer
availability in the background

Influence:
1. Open the print preview dialog and verify it appears immediately
without freezing
2. Confirm the cancel button works while printer info is still loading
3. Test that setDocName, setPrintFromPath, and updateDialogSettingInfo
called before show are preserved and applied after startup
4. Verify async preview (setAsynPreview), n-up imposition, and page
range settings work when applied before startup
5. Test that closing the dialog during loading does not crash or hang
6. Verify reopening the dialog after cancel reuses the printer correctly
7. Test PDF export after the dialog has fully initialized
8. Verify that installed settings plugins receive pending setting info
in correct order

fix: 延迟打印机初始化直至就绪

1. 将耗时的打印机初始化(DPrinter 构造、默认打印机查询)从对话框构造函数
移入异步打印机信息获取流程
2. 添加 copyPrintSetting 辅助函数深拷贝 DPrintPreviewSettingInfo 对象,
避免调用方在启动完成前销毁设置对象导致的悬空引用
3. 对话框未初始化时缓存待处理的设置、文档名和打印路径,待打印机信息就绪
后统一应用
4. 在 event、eventFilter 和 setPrintFromPath 中添加 isInited 判断,避免
加载期间操作未初始化的 UI
5. 加载期间禁用滚动区域、预览控件和打印按钮,保持取消按钮可用以便用户随
时关闭
6. 使用 QSharedPointer<DPrinter> 管理所有权,安全释放对话框在构造过程中
关闭时被丢弃的结果
7. 在工作线程查询前预初始化 QPlatformPrinterSupportPlugin,避免插件的非
同步访问
8. 新增 DefaultPrinterInfo 结构,从工作线程同时返回打印机名称和已构造
的 DPrinter
9. 重新启用 ut_dprintpreviewdialog 单元测试,补充加载状态、待处理设置、
异步预览、PDF 导出及取消重开等场景覆盖

Log: 打印预览对话框现可即时打开并在后台校验打印机可用性

Influence:
1. 打开打印预览对话框,确认立即可见且无卡顿
2. 确认在打印机信息加载期间取消按钮可正常使用
3. 测试在 show 之前调用 setDocName、setPrintFromPath 和
updateDialogSettingInfo 时,相关设置被保留并在启动后生效
4. 验证在启动前设置的异步预览(setAsynPreview)、N 合一拼版和页码范围设
置均能正确生效
5. 测试加载期间关闭对话框不会崩溃或卡死
6. 验证取消后重新打开对话框能正确复用打印机
7. 测试对话框完全初始化后的 PDF 导出功能
8. 验证已安装的设置插件按正确顺序收到待处理设置信息

PMS:BUG-375053
@github-actions

Copy link
Copy Markdown
Contributor
  • 检测到敏感词export变动
详情
    {
    "export": {
        "tests/testcases/widgets/ut_dprintpreviewdialog.cpp": {
            "b": [
                "    painter.drawText(QPointF(20, 20), QStringLiteral(\"PDF export after asynchronous startup\"));"
            ]
        }
    }
}

@sourcery-ai

sourcery-ai Bot commented Oct 28, 2026

Copy link
Copy Markdown

Reviewer's Guide

The dialog now appears without blocking on printer initialization by fetching printer information and constructing the printer asynchronously, safely buffering pre-startup requests and protecting UI and ownership lifecycles; comprehensive tests cover loading, cancellation, deferred settings, preview behavior, export, and reopening.

Sequence diagram for asynchronous print preview dialog startup

sequenceDiagram
    actor User
    participant Dialog as DPrintPreviewDialog
    participant Worker as PrinterInfoWorker
    participant Plugin as QPlatformPrinterSupportPlugin
    participant Printer as DPrinter
    participant UI as PrintPreviewUI

    User->>Dialog: show()
    Dialog->>UI: initui()
    Dialog->>UI: Disable scrollarea, preview, and print button
    Dialog->>Dialog: fetchPrinterInfo()
    Dialog->>Plugin: get()
    Dialog->>Worker: Query printer names and default printer
    Worker->>Printer: Construct DPrinter()
    Worker-->>Dialog: DefaultPrinterInfo(name, printer)
    Worker-->>Dialog: Printer names
    Dialog->>Dialog: applyPrinterInfo()
    Dialog->>UI: Attach printer and initialize data/connections
    Dialog->>UI: Enable controls
    Dialog-->>User: Ready for printing
Loading

Sequence diagram for deferred print preview settings

sequenceDiagram
    actor Caller
    participant Dialog as DPrintPreviewDialog
    participant Pending as PendingSettings
    participant Startup as PrinterStartup
    participant Helper as PreviewSettingsPluginHelper
    participant Preview as DPrintPreviewWidget

    Caller->>Dialog: setDocName()
    Dialog->>Pending: Store pendingDocName
    Caller->>Dialog: setPrintFromPath()
    Dialog->>Pending: Queue print path in preview
    Caller->>Dialog: updateDialogSettingInfo(info)
    Dialog->>Dialog: copyPrintSetting(info)
    Dialog->>Pending: Store deep copy
    Startup-->>Dialog: Printer information ready
    Dialog->>Helper: updateSettingInfo(pending setting)
    Dialog->>Preview: updatePreview()
    Dialog->>Pending: Clear pendingSettings
Loading

State diagram for print preview dialog loading and cancellation

stateDiagram-v2
    [*] --> Loading: show()
    Loading --> Loading: setDocName / setPrintFromPath / updateDialogSettingInfo
    Loading --> Ready: applyPrinterInfo()
    Loading --> Closed: cancelBtn clicked
    Ready --> Closed: reject()
    Closed --> Loading: show()
    Ready --> Ready: setAsynPreview / n-up / page range
    Ready --> Closed: finished()
    Loading --> [*]: dialog destroyed
    Closed --> [*]
Loading

File-Level Changes

Change Details Files
Defers printer construction and availability queries to asynchronous startup while making worker results safe to apply or discard.
  • Moves DPrinter creation and default-printer lookup into QtConcurrent work.
  • Pre-initializes platform printer support before worker-thread queries.
  • Returns printer name and shared printer ownership through DefaultPrinterInfo.
  • Cancels and safely discards in-flight printer work when the dialog finishes or is destroyed.
src/widgets/dprintpreviewdialog.cpp
src/widgets/private/dprintpreviewdialog_p.h
Adds a loading-state lifecycle that keeps the dialog responsive and replays API calls made before initialization.
  • Disables preview controls and printing while retaining an active cancel button.
  • Queues document name, print path, and deep-copied setting objects until printer information is ready.
  • Applies pending settings in order after initialization and guards event/UI operations against uninitialized state.
  • Makes preview widget initialization and updates tolerate a missing printer.
src/widgets/dprintpreviewdialog.cpp
src/widgets/dprintpreviewwidget.cpp
include/widgets/dprintpreviewwidget.h
src/widgets/private/dprintpreviewdialog_p.h
Re-enables and expands dialog tests for asynchronous startup and deferred operations.
  • Adds coverage for responsive loading, cancellation, destruction during loading, and cancel/reopen behavior.
  • Verifies deferred document names, paths, settings, asynchronous preview, n-up/page-range behavior, and PDF export.
  • Restores the dialog test source to the widget test build.
tests/CMakeLists.txt
tests/testcases/widgets/ut_dprintpreviewdialog.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 99 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 本次提交将 DPrintPreviewDialog 的打印机初始化推迟到异步任务完成后执行, 修复了启动时阻塞 UI 的问题。代码逻辑正确,空指针保护完善, 使用 QSharedPointer 替代裸指针管理 DPrinter 生命周期, 线程安全考虑周到(提前初始化插件单例、使用线程安全的 QSharedPointer 引用计数), 测试覆盖全面(7 个测试用例覆盖加载态响应、pending 设置保持、异步预览、销毁、PDF 导出、取消重开等场景)。 仅 copyPrintSetting 工厂函数存在轻微的重复模式,整体质量优秀。

🔍 详细分析

1. 语法逻辑 ✅

评价: 语法正确,逻辑清晰 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


2. 代码质量 ✅

评价: 代码结构清晰,注释完整 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['copyPrintSetting() 可考虑使用模板函数或宏简化重复的 new + static_cast 模式,但当前写法清晰可维护,优先级低']


3. 代码性能 ✅

评价: 性能良好,资源使用合理 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


4. 代码安全 🔒

评价: 存在0个安全漏洞 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: []


💡 改进建议代码示例

// copyPrintSetting 可选的模板化简化方案(非必须,当前写法已可维护):
template<typename T>
static DPrintPreviewSettingInfo *cloneSetting(const DPrintPreviewSettingInfo *info) {
    return new T(*static_cast<const T *>(info));
}

static DPrintPreviewSettingInfo *copyPrintSetting(const DPrintPreviewSettingInfo *info)
{
    switch (info->type()) {
    case DPrintPreviewSettingInfo::PS_Printer:       return cloneSetting<DPrintPreviewPrinterInfo>(info);
    case DPrintPreviewSettingInfo::PS_Copies:        return cloneSetting<DPrintPreviewCopiesInfo>(info);
    case DPrintPreviewSettingInfo::PS_PageRange:     return cloneSetting<DPrintPreviewPageRangeInfo>(info);
    case DPrintPreviewSettingInfo::PS_Orientation:   return cloneSetting<DPrintPreviewOrientationInfo>(info);
    case DPrintPreviewSettingInfo::PS_PaperSize:     return cloneSetting<DPrintPreviewPaperSizeInfo>(info);
    case DPrintPreviewSettingInfo::PS_PrintDuplex:   return cloneSetting<DPrintPreviewPrintDuplexInfo>(info);
    case DPrintPreviewSettingInfo::PS_NUpPrinting:   return cloneSetting<DPrintPreviewNUpPrintInfo>(info);
    case DPrintPreviewSettingInfo::PS_PageOrder:     return cloneSetting<DPrintPreviewPageOrderInfo>(info);
    case DPrintPreviewSettingInfo::PS_ColorMode:     return cloneSetting<DPrintPreviewColorModeInfo>(info);
    case DPrintPreviewSettingInfo::PS_PaperMargins:  return cloneSetting<DPrintPreviewPaperMarginsInfo>(info);
    case DPrintPreviewSettingInfo::PS_Scaling:       return cloneSetting<DPrintPreviewScalingInfo>(info);
    case DPrintPreviewSettingInfo::PS_Watermark:     return cloneSetting<DPrintPreviewWatermarkInfo>(info);
    default:                                          return nullptr;
    }
}

本报告由 AI 代码审查工具自动生成

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, wjyrich

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@github-actions

Copy link
Copy Markdown
Contributor
  • 检测到敏感词export变动
详情
    {
    "export": {
        "tests/testcases/widgets/ut_dprintpreviewdialog.cpp": {
            "b": [
                "    painter.drawText(QPointF(20, 20), QStringLiteral(\"PDF export after asynchronous startup\"));"
            ]
        }
    }
}

@wjyrich

wjyrich commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot

deepin-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

This pr force merged! (status: blocked)

@deepin-bot
deepin-bot Bot merged commit 467c463 into linuxdeepin:master Oct 28, 2026
21 of 24 checks passed
defaultPrinterWatcher->setFuture(QtConcurrent::run([createPrinter = !printer] {
qDebug(dPrintPreview) << "Default printer query started";
DefaultPrinterInfo info;
if (createPrinter) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个一直为true吧,QPrinterInfo::defaultPrinterName没有被请求吧,

printBtn->setEnabled(false);

// Initialize Qt's unguarded plugin singleton before querying it in workers.
QPlatformPrinterSupportPlugin::get();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个会引起警告吧,是不是要加unused宏,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants