You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

开发双PDF同窗口渲染程序遇到三类技术问题求助

试卷与答卷PDF同窗口渲染问题修复

我正在开发一款程序,用于导入试卷PDF和答卷PDF两份文件,要求将二者在同一窗口中渲染并支持垂直滚动。以下是修复后的Python代码:

import tkinter as tk
from tkinter import filedialog
from tkinter import ttk
from PIL import Image, ImageTk
import os
from pdf2image import convert_from_path

class PDFViewerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("PDF Question Cropper")

        self.frame = ttk.Frame(self.root)
        self.frame.pack(fill="both", expand=True)

        self.question_file = None
        self.answer_file = None

        self.question_label = ttk.Label(self.frame, text="No question file selected.")
        self.question_label.pack(pady=5)

        self.select_question_button = ttk.Button(self.frame, text="Select Question PDF", command=self.select_question_pdf)
        self.select_question_button.pack(pady=5)

        self.answer_label = ttk.Label(self.frame, text="No answer file selected.")
        self.answer_label.pack(pady=5)

        self.select_answer_button = ttk.Button(self.frame, text="Select Answer PDF", command=self.select_answer_pdf)
        self.select_answer_button.pack(pady=5)

        self.confirm_button = ttk.Button(self.frame, text="Confirm and Process", command=self.process_pdfs)
        self.confirm_button.pack(pady=10)

        self.new_window = None
        self.pdf_canvas1 = None
        self.pdf_canvas2 = None
        self.photo_list1 = []  # 保存图片引用,避免被垃圾回收
        self.photo_list2 = []

    def select_question_pdf(self):
        self.question_file = filedialog.askopenfilename(title="Select Question PDF", filetypes=[("PDF Files", "*.pdf")])
        if self.question_file:
            self.question_label.config(text=f"Question PDF: {os.path.basename(self.question_file)}")

    def select_answer_pdf(self):
        self.answer_file = filedialog.askopenfilename(title="Select Answer PDF", filetypes=[("PDF Files", "*.pdf")])
        if self.answer_file:
            self.answer_label.config(text=f"Answer PDF: {os.path.basename(self.answer_file)}")

    def process_pdfs(self):
        if not self.question_file or not self.answer_file:
            return
        self.open_new_window()

    def open_new_window(self):
        self.new_window = tk.Toplevel(self.root)
        self.new_window.title("Question & Answer Viewer")
        self.new_window.geometry("1200x800")

        self.pdf_viewer_frame = ttk.Frame(self.new_window)
        self.pdf_viewer_frame.pack(fill=tk.BOTH, expand=True)

        # 左侧试卷区域:画布+滚动条
        left_frame = ttk.Frame(self.pdf_viewer_frame)
        left_frame.pack(side="left", fill="both", expand=True, padx=5, pady=5)

        self.pdf_canvas1 = tk.Canvas(left_frame)
        self.scrollbar1 = ttk.Scrollbar(left_frame, orient="vertical", command=self.pdf_canvas1.yview)
        self.scrollbar1.pack(side="right", fill="y")
        self.pdf_canvas1.config(yscrollcommand=self.scrollbar1.set)
        self.pdf_canvas1.pack(side="left", fill="both", expand=True)

        # 右侧答卷区域:画布+滚动条
        right_frame = ttk.Frame(self.pdf_viewer_frame)
        right_frame.pack(side="right", fill="both", expand=True, padx=5, pady=5)

        self.pdf_canvas2 = tk.Canvas(right_frame)
        self.scrollbar2 = ttk.Scrollbar(right_frame, orient="vertical", command=self.pdf_canvas2.yview)
        self.scrollbar2.pack(side="right", fill="y")
        self.pdf_canvas2.config(yscrollcommand=self.scrollbar2.set)
        self.pdf_canvas2.pack(side="left", fill="both", expand=True)

        self.file_label = ttk.Label(self.new_window, text="Processing...")
        self.file_label.pack(pady=5)

        self.display_pdfs()

    def display_pdfs(self):
        if not self.question_file or not self.answer_file:
            return

        question_pages = convert_from_path(self.question_file)
        answer_pages = convert_from_path(self.answer_file)

        if not question_pages or not answer_pages:
            self.file_label.config(text="No pages found in selected PDFs.")
            return

        # 统一画布宽度为两份PDF的最大页面宽度
        max_width = max(page.width for page in question_pages + answer_pages)
        # 计算总高度为两份PDF的最大总页数高度
        total_height = max(sum(p.height for p in question_pages), sum(p.height for p in answer_pages))

        # 拼接试卷所有页面为长图
        question_full_img = Image.new('RGB', (max_width, total_height), (255, 255, 255))
        current_y = 0
        for page in question_pages:
            question_full_img.paste(page, (0, current_y))
            current_y += page.height

        # 拼接答卷所有页面为长图
        answer_full_img = Image.new('RGB', (max_width, total_height), (255, 255, 255))
        current_y = 0
        for page in answer_pages:
            answer_full_img.paste(page, (0, current_y))
            current_y += page.height

        # 转换为Tkinter可用格式
        photo1 = ImageTk.PhotoImage(image=question_full_img, master=self.new_window)
        photo2 = ImageTk.PhotoImage(image=answer_full_img, master=self.new_window)

        # 保存引用防止被回收
        self.photo_list1.append(photo1)
        self.photo_list2.append(photo2)

        # 设置画布滚动区域和显示尺寸
        self.pdf_canvas1.config(scrollregion=(0, 0, max_width, total_height), width=max_width//2)
        self.pdf_canvas2.config(scrollregion=(0, 0, max_width, total_height), width=max_width//2)

        # 绘制完整长图
        self.pdf_canvas1.create_image(0, 0, image=photo1, anchor=tk.NW)
        self.pdf_canvas2.create_image(0, 0, image=photo2, anchor=tk.NW)

        # 更新状态标签
        self.file_label.config(
            text=f"Question PDF: {os.path.basename(self.question_file)} ({len(question_pages)} pages)\n"
                 f"Answer PDF: {os.path.basename(self.answer_file)} ({len(answer_pages)} pages)"
        )

if __name__ == "__main__":
    root = tk.Tk()
    app = PDFViewerApp(root)
    root.mainloop()

问题修复说明

1. 两份文件显示空间不一致

  • 为试卷和答卷分别创建独立子框架,每个子框架包含画布和对应滚动条
  • 两个子框架均设置fill="both", expand=True,确保平分窗口空间
  • 添加内边距避免内容紧贴窗口边缘

2. 滚动条位置错误

  • 将每个滚动条与对应画布放在同一子框架内,滚动条固定在子框架右侧
  • 实现滚动条与对应文件的一一对应,不再重叠挤在窗口最右侧

3. 仅显示最后一页

  • 将所有PDF页面拼接成单张长图,一次性绘制到画布上
  • 用列表保存图片引用,避免Tkinter垃圾回收机制删除图片
  • 设置画布滚动区域为长图完整尺寸,支持垂直滚动浏览全部页面

内容的提问来源于stack exchange,提问作者jonathanlin1112

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 20:39:57