Qt打印功能实战:从QPrintDialog到QPrintPreviewWidget的完整指南
1. 为什么你的Qt应用需要一个好用的打印功能做桌面应用开发尤其是那些需要处理文档、报表或者票据的应用打印功能几乎是一个绕不开的坎。我记得几年前接手一个项目用户需要在软件里查看数据报表然后直接打印出来存档。一开始我们团队觉得这还不简单不就是调用系统打印对话框把内容画上去嘛。结果真做起来才发现坑一个接一个打印出来的内容对不齐、分页错乱、预览效果和实际打印天差地别用户抱怨连连。后来我们花了大力气深入研究Qt的打印模块才发现它其实提供了一套非常完整且强大的工具链从最简单的弹窗打印到复杂的、可高度自定义的预览界面都能搞定。今天我就把自己这些年踩过的坑和总结的经验结合QPrintDialog、QPrintPreviewDialog和QPrintPreviewWidget这三个核心组件给你掰开揉碎了讲清楚。无论你是想快速实现一个“打印”按钮还是需要打造一个媲美WPS、Office的专业级打印预览界面这篇文章都能给你一条清晰的路径。简单来说Qt的打印支持模块printsupport就像是一个桥梁一头连着你的应用数据比如表格、文本、图表另一头连着系统的打印机或者虚拟打印机比如“打印成PDF”。它的核心任务有两个一是让你能方便地配置打印参数用什么纸、横着打还是竖着打、打几份二是提供一个画布QPrinter让你用熟悉的QPainter像在屏幕上绘图一样把内容“画”到纸张上。我们接下来要讲的三个类就是围绕这两个核心任务展开的不同“包装”和“增强”。2. 第一步用QPrintDialog实现基础打印万事开头难但Qt让打印的第一步变得异常简单。QPrintDialog就是那个最直接、最标准的系统打印对话框。它的主要任务就是让用户选择打印机、设置纸张、调整页边距和打印份数。你几乎不需要自己画任何UI。2.1 环境准备与基本调用首先别忘了在项目的.pro文件里加上那句“咒语”QT printsupport没有它后面所有关于打印的代码都会编译报错。接下来我们看看如何在一个按钮的点击事件里弹出打印对话框。我习惯把打印相关的操作封装在一个独立的函数里比如叫onPrintButtonClicked。#include QPrintDialog #include QPrinter #include QPainter #include QDebug void MainWindow::onPrintButtonClicked() { // 1. 创建打印机对象这是所有打印操作的基石 QPrinter printer; // 2. 创建打印对话框并传入我们的打印机对象 QPrintDialog printDialog(printer, this); printDialog.setWindowTitle(tr(打印文档)); // 3. 弹出对话框并等待用户操作 if (printDialog.exec() QDialog::Accepted) { // 用户点击了“打印” qDebug() 用户选择了打印; startPrinting(printer); } else { // 用户点击了“取消” qDebug() 用户取消了打印。; } }这段代码的核心是printDialog.exec()。它会阻塞当前线程弹出一个模态对话框。这个对话框的外观和功能取决于你的操作系统Windows、macOS、Linux各有不同但基本功能都一样。当用户点击“打印”时exec()返回QDialog::Accepted通常是1点击“取消”或关闭窗口则返回QDialog::Rejected通常是0。2.2 获取用户设置并执行打印用户在那个对话框里可不是瞎点的他的每一个选择都会反馈到我们传入的QPrinter对象里。所以在调用startPrinting函数开始真正的绘制之前printer对象已经包含了所有必要的打印设置信息。我们来看看在startPrinting函数里能做些什么void MainWindow::startPrinting(QPrinter *printer) { // 打印前先看看用户都设置了啥 qDebug() --- 打印参数详情 ---; qDebug() 打印机名称 printer-printerName(); qDebug() 纸张大小 printer-pageLayout().pageSize().name(); qDebug() 页面方向 (printer-pageLayout().orientation() QPageLayout::Portrait ? 纵向 : 横向); qDebug() 打印份数 printer-copyCount(); qDebug() 页边距(左,上,右,下,单位点) printer-pageLayout().margins(); // 开始绘制 QPainter painter; if (!painter.begin(printer)) { qWarning() 无法启动打印绘制; return; } // 假设我们打印一个简单的文本和矩形 QRectF pageRect printer-pageRect(QPrinter::Point); // 获取可打印区域去掉页边距 painter.setFont(QFont(Arial, 12)); painter.drawText(pageRect, Qt::AlignTop | Qt::AlignLeft, 这是打印测试内容。); // 画一个边框 painter.setPen(Qt::black); painter.drawRect(pageRect.adjusted(10, 10, -10, -10)); painter.end(); qDebug() 打印任务已发送。; }这里有几个关键点printer-pageLayout()这是获取页面布局信息的入口包含纸张大小、方向、页边距等。pageRect方法非常有用它返回的是扣除页边距后的实际可绘制区域。直接用这个矩形来规划你的内容布局能有效避免内容被裁切。QPainter painter(printer)一旦painter通过begin方法关联了printer你所有用painter进行的绘制操作其目标就不再是屏幕而是打印机或PDF文件。你可以画文本、线条、形状、图片和使用QPainter在QPixmap或QWidget上绘图完全一样。单位注意pageRect我指定了QPrinter::Point作为单位。在打印中常用的单位有Point点1/72英寸、Millimeter毫米、Inch英寸等。使用Point是Qt打印中的一种常见做法因为它和字体大小等单位系统配合得比较好。你也可以用printer-setPageSizeMM()或setPageSize来设置。踩坑提醒QPrintDialog虽然方便但它是一次性的。用户设置完点击打印对话框关闭设置信息就保存在QPrinter对象里用于本次打印。如果你需要记住用户的偏好比如总是用A4纸横向打印需要在每次弹出对话框前用printer-setPageLayout(...)等方法预先设置好默认值。3. 进阶用QPrintPreviewDialog实现“所见即所得”预览直接打印就像闭着眼睛投篮命中率全靠运气。用户更希望先看看效果再决定是否打印这就是预览功能的价值。QPrintPreviewDialog是Qt提供的一个开箱即用的预览解决方案。它自带完整的UI工具栏缩放、单页/双页视图、页面导航、打印按钮和预览区域。3.1 基本集成与信号槽连接使用QPrintPreviewDialog的流程和QPrintDialog略有不同。它不是让你直接绘制而是通过一个信号来“请求”你绘制。#include QPrintPreviewDialog void MainWindow::onPrintPreviewButtonClicked() { QPrinter printer; // 可以预先设置一些默认参数比如方向 printer.setPageOrientation(QPageLayout::Landscape); // 创建预览对话框 QPrintPreviewDialog previewDialog(printer, this); previewDialog.setWindowFlags(previewDialog.windowFlags() | Qt::WindowMaximizeButtonHint); previewDialog.setWindowTitle(tr(打印预览)); // 关键一步连接paintRequested信号 connect(previewDialog, QPrintPreviewDialog::paintRequested, this, MainWindow::renderForPreview); // 显示对话框 previewDialog.exec(); }核心就在于paintRequested信号。当预览对话框需要显示某一页的内容时比如用户缩放、翻页、改变了纸张设置它就会发出这个信号并携带一个QPrinter*参数。你的任务就是在这个信号的槽函数里完成所有页面的绘制。3.2 实现多页内容绘制槽函数renderForPreview是展示你打印逻辑的地方。这里要处理多页内容。void MainWindow::renderForPreview(QPrinter *printer) { QPainter painter(printer); // 获取页面尺寸信息 QPageLayout layout printer-pageLayout(); QRectF pageRect layout.paintRect(QPageLayout::Point); // 可绘制区域 qDebug() 正在为预览渲染页面纸张 layout.pageSize().name(); // 模拟渲染一个多页文档比如一个长表格 QStringList dataLines generateReportData(); // 假设这个函数生成很多行数据 int linesPerPage calculateLinesPerPage(painter.font(), pageRect.height()); int currentLine 0; int pageNumber 1; bool isFirstPage true; while (currentLine dataLines.size()) { if (!isFirstPage) { // 如果不是第一页需要调用newPage()来创建新页面 if (!printer-newPage()) { qWarning() 创建新页面失败; break; } } isFirstPage false; // 绘制页眉 painter.drawText(pageRect, Qt::AlignTop | Qt::AlignHCenter, QString(报表 - 第 %1 页).arg(pageNumber)); // 绘制本页内容 QRectF contentRect pageRect.adjusted(50, 50, -50, -50); // 内容区域 for (int i 0; i linesPerPage currentLine dataLines.size(); i, currentLine) { QPointF linePos(contentRect.left(), contentRect.top() i * 20); painter.drawText(linePos, dataLines.at(currentLine)); } // 绘制页脚 painter.drawText(pageRect, Qt::AlignBottom | Qt::AlignRight, QDate::currentDate().toString(yyyy-MM-dd)); pageNumber; } qDebug() 预览渲染完成共 pageNumber-1 页。; }这里有几个非常重要的细节printer-newPage()这是分页的关键。当你画完一页的内容调用此方法告诉打印机“这一页结束了准备下一张纸”。它的返回值很重要如果为false通常意味着打印过程被取消或出错。页眉页脚预览和最终打印的绘制逻辑必须完全一致。所以像页眉、页脚、页码这些每页都有的元素要在绘制循环里处理。内容计算calculateLinesPerPage这样的函数是必要的。你需要根据字体大小、行间距和页面可用高度精确计算出每页能放多少行数据否则内容会重叠或被切断。QPrintPreviewDialog的优势是省心UI现成。但它的界面是固定的如果你需要更个性化的预览界面比如把预览窗口嵌入到自己的应用标签页里或者想自定义工具栏按钮那就需要用到更底层的QPrintPreviewWidget了。4. 高手之路深度定制QPrintPreviewWidgetQPrintPreviewWidget才是真正的“预览引擎”。它本身只是一个QWidget只负责显示预览图像和接收一些基本的页面导航指令。工具栏、缩放控制、页面布局切换等所有UI都需要你自己来构建和连接。这带来了极大的灵活性。4.1 将预览窗口嵌入你的界面假设我们想做一个类似WPS的界面左侧是文档树右侧上方是工具栏下方是预览区域。首先在界面设计时比如在Qt Designer里放置一个普通的QWidget容器例如一个QFrame并提升为QPrintPreviewWidget。或者在代码中直接创建。// 在MainWindow的构造函数或某个初始化函数中 void MainWindow::setupPrintPreviewTab() { // 创建打印机和预览部件 m_printer new QPrinter(QPrinter::HighResolution); m_previewWidget new QPrintPreviewWidget(m_printer, this); // 将预览部件添加到界面布局中 ui-previewContainerLayout-addWidget(m_previewWidget); // 连接预览部件的信号 connect(m_previewWidget, QPrintPreviewWidget::paintRequested, this, MainWindow::renderForPreview); // 复用之前的渲染函数 // 创建自定义工具栏 createCustomToolBar(); }4.2 构建自定义工具栏并连接功能现在来创建createCustomToolBar函数实现我们自己的控制逻辑。void MainWindow::createCustomToolBar() { // 假设ui-previewToolBar是一个QToolBar QAction *zoomInAction ui-previewToolBar-addAction(QIcon(:/icons/zoom_in.png), 放大); QAction *zoomOutAction ui-previewToolBar-addAction(QIcon(:/icons/zoom_out.png), 缩小); QAction *fitWidthAction ui-previewToolBar-addAction(QIcon(:/icons/fit_width.png), 适合宽度); QAction *fitPageAction ui-previewToolBar-addAction(QIcon(:/icons/fit_page.png), 适合页面); ui-previewToolBar-addSeparator(); QComboBox *zoomCombo new QComboBox; zoomCombo-setEditable(true); zoomCombo-addItems(QStringList() 25% 50% 75% 100% 150% 200% 400%); zoomCombo-setCurrentText(100%); ui-previewToolBar-addWidget(zoomCombo); QAction *printAction ui-previewToolBar-addAction(QIcon(:/icons/print.png), 打印); QAction *pageSetupAction ui-previewToolBar-addAction(QIcon(:/icons/page_setup.png), 页面设置); // 连接信号槽 connect(zoomInAction, QAction::triggered, m_previewWidget, QPrintPreviewWidget::zoomIn); connect(zoomOutAction, QAction::triggered, m_previewWidget, QPrintPreviewWidget::zoomOut); connect(fitWidthAction, QAction::triggered, m_previewWidget, QPrintPreviewWidget::fitToWidth); connect(fitPageAction, QAction::triggered, m_previewWidget, QPrintPreviewWidget::fitInView); connect(zoomCombo, QComboBox::currentTextChanged, this, [this](const QString text){ bool ok; float factor text.replace(%, ).toFloat(ok) / 100.0; if (ok) { m_previewWidget-setZoomFactor(factor); } }); connect(printAction, QAction::triggered, this, MainWindow::onCustomPrint); connect(pageSetupAction, QAction::triggered, this, MainWindow::onPageSetup); } void MainWindow::onCustomPrint() { QPrintDialog dialog(m_printer, this); if (dialog.exec() QDialog::Accepted) { // 用户确认了打印设置直接调用渲染函数进行打印 // 注意这里传入的printer和预览用的是同一个所以设置会保持一致 renderForPreview(m_printer); } } void MainWindow::onPageSetup() { QPageSetupDialog dialog(m_printer, this); if (dialog.exec() QDialog::Accepted) { // 页面设置已更新需要更新预览 m_previewWidget-updatePreview(); } }通过这种方式你完全掌控了预览界面的外观和交互逻辑。QPrintPreviewWidget提供了丰富的内置槽函数如zoomIn,fitToWidth等你可以直接连接按钮。setZoomFactor可以设置任意缩放比例。updatePreview方法会在页面设置、打印机等变更后强制刷新预览重新触发paintRequested信号。4.3 处理页面导航与显示模式除了缩放QPrintPreviewWidget还支持多种视图模式。// 在自定义工具栏中添加视图模式按钮 QActionGroup *viewModeGroup new QActionGroup(this); QAction *singleModeAction viewModeGroup-addAction(ui-previewToolBar-addAction(单页)); QAction *facingModeAction viewModeGroup-addAction(ui-previewToolBar-addAction(双页)); singleModeAction-setCheckable(true); facingModeAction-setCheckable(true); singleModeAction-setChecked(true); connect(singleModeAction, QAction::triggered, this, [this](){ m_previewWidget-setViewMode(QPrintPreviewWidget::SinglePageView); }); connect(facingModeAction, QAction::triggered, this, [this](){ m_previewWidget-setViewMode(QPrintPreviewWidget::FacingPagesView); }); // 添加页面导航 QLabel *pageLabel new QLabel(页码); ui-previewToolBar-addWidget(pageLabel); QSpinBox *pageSpinBox new QSpinBox; pageSpinBox-setMinimum(1); pageSpinBox-setMaximum(100); // 最大值可以动态更新 ui-previewToolBar-addWidget(pageSpinBox); connect(pageSpinBox, QOverloadint::of(QSpinBox::valueChanged), this, [this](int page){ m_previewWidget-setCurrentPage(page); }); // 当总页数变化时在renderForPreview中计算更新SpinBox的最大值setViewMode可以切换单页、双页并排等预览模式。setCurrentPage用于跳转到指定页。你还可以通过pageCount信号来获取总页数动态更新导航控件。5. 实战技巧与避坑指南掌握了三大组件的基本用法我们再来聊聊那些能让你的打印功能从“能用”到“好用”的实战技巧和常见陷阱。5.1 高分辨率与抗锯齿处理打印机尤其是激光打印机和导出PDF的分辨率远高于屏幕通常是300 DPI甚至600 DPI。如果你直接用屏幕绘图的逻辑文字和线条可能会显得粗糙。void MainWindow::renderForPreview(QPrinter *printer) { QPainter painter(printer); // 启用抗锯齿使曲线和文字边缘更平滑 painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); // 对于线条可以设置更细的笔。注意单位转换。 // 假设我们想要0.2mm的线宽 qreal physicalLineWidth 0.2; // mm qreal pointWidth physicalLineWidth * 72.0 / 25.4; // 转换为点Point QPen pen(Qt::black); pen.setWidthF(pointWidth); // 使用浮点数设置宽度 painter.setPen(pen); // 绘制内容... }设置Antialiasing和TextAntialiasing对打印质量提升非常明显。同时使用setWidthF来精确控制线宽而不是简单的setWidth(1)这能确保在不同DPI的设备上输出一致的物理尺寸。5.2 复杂文档的分页与定位打印长表格或多章节文档时分页逻辑是难点。一个稳健的方法是先进行“虚拟布局计算”。// 一个简化的示例计算每页内容的位置 QListQRectF MainWindow::calculatePageLayouts(QPrinter *printer, const ReportData data) { QListQRectF pageContentRects; QPageLayout layout printer-pageLayout(); QRectF usableRect layout.paintRect(QPageLayout::Millimeter); // 使用毫米单位计算 qreal currentY usableRect.top(); const qreal lineHeight 5.0; // 假设每行高5mm const qreal marginBottom 10.0; // 页脚预留10mm for (const ReportItem item : data.items) { qreal itemHeight calculateItemHeight(item); // 计算这个数据项需要的高度 if (currentY itemHeight usableRect.bottom() - marginBottom) { // 当前页放不下了结束当前页开始新的一页 pageContentRects.append(QRectF(usableRect.left(), usableRect.top(), usableRect.width(), currentY - usableRect.top())); currentY usableRect.top(); } // 记录这个item的位置 (currentY) // ... currentY itemHeight lineSpacing; } // 添加最后一页 if (currentY usableRect.top()) { pageContentRects.append(QRectF(usableRect.left(), usableRect.top(), usableRect.width(), currentY - usableRect.top())); } return pageContentRects; }在真正的renderForPreview函数中你就可以根据计算好的pageContentRects列表精确地知道每页应该画哪些内容以及它们在页面上的位置从而避免复杂的现场计算和判断。5.3 处理打印取消与进度反馈对于页数很多的文档打印或生成预览可能需要时间。好的用户体验应该提供取消操作和进度反馈。QPrinter本身支持异步操作但需要结合多线程。一个更简单的方案是在渲染函数中定期检查一个标志位。// 在MainWindow类中声明一个原子布尔标志 std::atomicbool m_printCanceled{false}; void MainWindow::onPrintButtonClicked() { m_printCanceled false; QProgressDialog progress(正在准备打印..., 取消, 0, 100, this); progress.setWindowModality(Qt::WindowModal); // 启动一个线程或使用QtConcurrent来执行打印任务 // 在线程中定期更新进度并检查m_printCanceled } void MainWindow::renderForPreview(QPrinter *printer) { QPainter painter(printer); // ... 在每页绘制开始前或结束后检查 for (int page 0; page totalPages; page) { if (m_printCanceled) { painter.end(); // 提前结束绘制 return; } // 绘制该页... // 更新进度例如通过信号发射 emit printProgress((page 1) * 100 / totalPages); } }对于QPrintPreviewWidget由于其paintRequested是在GUI线程中回调的长时间渲染会阻塞界面。对于极端复杂的文档可以考虑先将每一页渲染到高分辨率的QImage上缓存起来然后在paintRequested中快速绘制这些图像。5.4 导出为PDF或图片Qt的打印模块一个巨大的优势是打印到物理打印机和打印到PDF/图片文件代码几乎完全一样。你只需要在创建QPrinter对象时指定输出格式和文件名。// 导出为PDF QPrinter pdfPrinter(QPrinter::HighResolution); pdfPrinter.setOutputFormat(QPrinter::PdfFormat); pdfPrinter.setOutputFileName(/path/to/output.pdf); pdfPrinter.setPageSize(QPageSize(QPageSize::A4)); // ... 然后像正常打印一样使用QPainter在pdfPrinter上绘制 renderForPreview(pdfPrinter); // 导出为高分辨率图片例如用于生成报告缩略图 QPrinter imagePrinter(QPrinter::HighResolution); imagePrinter.setOutputFormat(QPrinter::NativeFormat); // 注意设置输出为图片需要一点技巧 // 更常见的做法是直接使用QPainter在QPixmap或QImage上绘制并设置其分辨率和尺寸。 QImage image(2480, 3508, QImage::Format_ARGB32); // A4 300 DPI image.fill(Qt::white); image.setDotsPerMeterX(300 * 100 / 2.54); // 设置DPI image.setDotsPerMeterY(300 * 100 / 2.54); QPainter imagePainter(image); // 使用与打印相同的绘制逻辑但坐标可能需要根据DPI调整掌握了这些技巧你就能应对绝大多数桌面应用的打印需求了。从简单的单据打印到复杂的多页报表预览Qt的这套打印框架都提供了坚实的支撑。关键在于理解QPrinter作为绘制目标QPainter作为绘制工具而QPrintDialog、QPrintPreviewDialog、QPrintPreviewWidget则是不同层级的用户交互封装。多动手写几个例子亲自调试一下页边距和分页很快就能得心应手。

相关新闻

【超全】基于微信小程序的儿童预防接种预约系统【包括源码+文档+调试】

【超全】基于微信小程序的儿童预防接种预约系统【包括源码+文档+调试】

💕💕发布人: 码上青云 💕💕各类成品Java毕设 。javaweb,ssm,springboot等项目,欢迎咨询。 💕💕程序开发、技术解答、代码讲解、文档, &#x1f31…

2026/7/4 8:44:44 阅读更多 →
软件测试之功能测试

软件测试之功能测试

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快一、测试项目启动与研读需求文档(一) 组建测试团队1、测试团队中的角色2、测试团队的基本责任尽早地发现软件程序、系统或产品中所有的问题。…

2026/7/4 12:28:46 阅读更多 →
国产电脑也能用!手把手教你编译最新Chromium ARM版(附视频解码修复技巧)

国产电脑也能用!手把手教你编译最新Chromium ARM版(附视频解码修复技巧)

国产ARM平台Chromium编译实战:从零构建到视频解码优化 最近几年,国产ARM架构的电脑逐渐进入更多开发者和技术爱好者的视野。无论是飞腾、龙芯还是其他基于ARM的国产平台,它们都面临着一个共同的挑战:软件生态,特别是浏…

2026/7/5 0:32:12 阅读更多 →

最新新闻

STM32F410RB与MC6470 IMU的高精度姿态控制实现

STM32F410RB与MC6470 IMU的高精度姿态控制实现

1. 项目背景与硬件选型解析在嵌入式系统开发中,精确的运动感知和控制能力是许多应用的核心需求。MC6470作为mCube推出的6自由度惯性测量单元(6DOF IMU),集成了三轴加速度计和三轴磁力计,能够提供完整的空间姿态数据。而STM32F410RB则是STMicr…

2026/7/5 7:34:11 阅读更多 →
MAX9744与PIC18F2455构建高效D类音频放大器方案

MAX9744与PIC18F2455构建高效D类音频放大器方案

1. 项目背景与核心组件解析在DIY音频设备改造和嵌入式音频系统开发中,功率放大器的选型直接影响最终音质表现。MAX9744作为一款高效D类音频功率放大器,搭配PIC18F2455微控制器的灵活控制能力,可以构建出性能优异且可编程的音频放大解决方案。…

2026/7/5 7:34:11 阅读更多 →
STM32与DS28EC20 1-Wire EEPROM嵌入式存储方案实战

STM32与DS28EC20 1-Wire EEPROM嵌入式存储方案实战

1. 项目背景与核心需求 在嵌入式系统开发中,持久化存储用户配置和偏好设置是一个经典需求。无论是工业控制设备、消费电子产品还是物联网终端,都需要在断电后仍能保留关键参数。传统方案如EEPROM或Flash存储各有局限——前者容量小、成本高,后…

2026/7/5 7:34:11 阅读更多 →
AppScan 10.0.1 安装部署全攻略:从证书导入到环境修复的避坑指南

AppScan 10.0.1 安装部署全攻略:从证书导入到环境修复的避坑指南

1. 项目概述:为什么AppScan的安装值得你认真对待如果你是一名安全工程师、渗透测试人员,或者正在负责公司应用系统的安全评估,那么IBM Security AppScan这个名字你一定不陌生。作为一款老牌且功能强大的Web应用动态安全测试(DAST&…

2026/7/5 7:32:10 阅读更多 →
STM32L152RE与25CSM04 EEPROM的高速数据检索优化方案

STM32L152RE与25CSM04 EEPROM的高速数据检索优化方案

1. 项目背景与核心需求在嵌入式系统开发中,数据检索的速度和精度往往成为系统性能的瓶颈。传统方案通常面临两个矛盾:要么使用低速但容量大的存储介质(如SD卡),要么选择高速但容量受限的片上Flash。25CSM04这款4Mb SPI…

2026/7/5 7:30:10 阅读更多 →
WindowsCleaner:彻底解决C盘爆红的终极清理工具,快速释放磁盘空间

WindowsCleaner:彻底解决C盘爆红的终极清理工具,快速释放磁盘空间

WindowsCleaner:彻底解决C盘爆红的终极清理工具,快速释放磁盘空间 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服! 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner 你是否经常遇到Windows电…

2026/7/5 7:30:10 阅读更多 →

日新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻