0

This code works fine and does exactly what I want but in the larger application that it is used in, when loading a new PDF or using the zoom in/zoom out buttons, the frame flickers not nearly as quickly as I would like or at all if possible. Basically, this takes in a pdf and uses the fitz library in Python to change it to an image and place it in a tkinter text widget using image_create. I would also like a better way of zooming but this was the only way I could think to do it.

Here's the code:

import tkinter as tk
from tkinter import filedialog
import ttkbootstrap as tb
import fitz

#----------------------------------------------------------

class PDFExample(tb.Frame):

    #------------------------------------------------------
    def __init__(self, root, filename=None):
        super().__init__(root)

        self.root = root
        self.filename = filename

        self.pack(fill='both', expand=True)

        self.dpi = 100

        # Setup
        self.setupViewer()

        self.checkSelection()

    #------------------------------------------------------
    def setupViewer(self):
        # Create 2 Frames
        self.pdf_frame = tb.Frame(self)
        self.pdf_frame.pack(side='left', fill='both',
            expand=1)

        self.button_frame = tb.Frame(self)
        self.button_frame.pack(side='right', fill='y')

        # Create PDF Viewer
        self.pdfViewer()

        # Create Buttons
        self.open_button = tb.Button(self.button_frame,
            text='Open PDF', command=self.open)
        self.open_button.grid(row=0, column=0,
            padx=5, pady=5, sticky='nsew')
        
        self.save_button = tb.Button(self.button_frame,
            text='Save PDF', command=self.save,
            state='disabled')
        self.save_button.grid(row=1, column=0,
            padx=5, pady=5, sticky='nsew')

        self.zoom_in = tb.Button(self.button_frame,
            text='Zoom In', command=self.zoomIn,
            state='disabled')
        self.zoom_in.grid(row=2, column=0,
            padx=5, pady=5, sticky='nsew')

        self.zoom_out = tb.Button(self.button_frame,
            text='Zoom Out', command=self.zoomOut,
            state='disabled')
        self.zoom_out.grid(row=3, column=0,
            padx=5, pady=5, sticky='nsew')

    #------------------------------------------------------
    def checkSelection(self):
        if self.filename is not None and os.file.exists(self.filename):
            self.save_button.config(state='enable')
            self.zoom_in.config(state='enable')
            self.zoom_out.config(state='enable')
            # Show PDF
            self.createPDF()
            self.addImage()

    #------------------------------------------------------
    def pdfViewer(self):
        scroll_y = tk.Scrollbar(self.pdf_frame,
            orient='vertical', width=16)
        scroll_x = tk.Scrollbar(self.pdf_frame,
            orient='horizontal', width=16)

        scroll_x.pack(side='bottom', fill='x')
        scroll_y.pack(side='right', fill='y')

        self.text = tb.Text(self.pdf_frame,
            yscrollcommand=scroll_y.set,
            xscrollcommand=scroll_x.set)
        self.text.pack(side='top', fill='both', expand=True)

        scroll_x.config(command=self.text.xview)
        scroll_y.config(command=self.text.yview)

    #------------------------------------------------------
    def open(self):
        files = [('PDF', '*.pdf'), ('All Files', '*.*')]
        ask_where = filedialog.askopenfile(filetypes=files,
            defaultextension='.pdf')
        self.filename = ask_where.name
        
        self.addImage()

    #------------------------------------------------------
    def save(self):
        files = [('PDF', '*.pdf'), ('All Files', '*.*')]
        ask_where = filedialog.asksaveasfile(filetypes=files,
            defaultextension='.pdf')

        shutil.copy(self.filename, ask_where.name)

    #------------------------------------------------------
    def zoomIn(self):
        self.dpi += 10
        self.addImage()

    #------------------------------------------------------
    def zoomOut(self):
        self.dpi -= 10
        self.addImage()

    #------------------------------------------------------
    def addImage(self):
        self.img_object_li = []

        with fitz.open(self.filename) as open_pdf:
            for page in open_pdf:
                pix = page.get_pixmap(dpi=self.dpi)
                pix1 = fitz.Pixmap(pix,0) if pix.alpha else pix
                img = pix1.tobytes('ppm')
                timg = tb.PhotoImage(data=img)
                self.img_object_li.append(timg)

        # Destroy PDF Viewer and Recreate in order to Zoom
        # There has to be a better way but this is all I
        # could figure out for now
        for widget in self.pdf_frame.winfo_children():
            widget.destroy()
        self.pdfViewer()

        for i in self.img_object_li:
            self.text.image_create('end', image=i)
            self.text.insert('end', '\n\n')
            self.text.configure(state='disabled')

#----------------------------------------------------------

if __name__ == '__main__':
    app = tb.Window(title='Test')

    p = PDFExample(app)

    app.position_center()

    app.mainloop()

Please ask any clarifying questions (I'm not always the best at explaining my code issues unless it's in person).

Thank you for any help!

blaufer
  • 117
  • 1
  • 3
  • 11
  • What is the current performance measurement and what is your desired one? You are loading entire pages in a event handler. Why not load pages in worker thread or process? Anyway why not use a PDF viewer, such as QPdfView? – relent95 Feb 08 '23 at 04:37
  • Didn't catch this until now, sorry. The PDF viewer is built into a larger tkinter program and as far as I can find, there are no currently working pdf viewers for tkinter. As for performance, I guess I just really want the look to be better than having the zoom buttons on the side and having to recreate the images each time (I know this isn't really an answer to that). – blaufer Feb 11 '23 at 17:08

0 Answers0