I am very new to python and want to build a gui that lets you skip through different pdfs. I managed to display the first pdf. My problem now is that I could not manage to display the second pdf and all following. If I only repeat the command to display the pdf, the new pdf gets displayed next to the old one, instead of replacing it. I have been through several hours of extensive googling and could not find out how to solve this. Can someone maybe help?
Here's my code:
from tkinter import *
import tkinter as tk
import glob
from tkPDFViewer import tkPDFViewer as pdf
from tkdocviewer import *
parent_path = 'somepath\\'
doc_list = glob.glob((parent_path + "*//*.pdf"))
doc_counter = 0
root = tk.Tk()
root.title('Training Data Creator')
root.geometry("1000x1000")
frame_r = Frame(root, relief=RAISED, borderwidth=1)
frame_r.pack(fill=BOTH, expand=True, side=tk.LEFT)
# creating object of ShowPdf from tkPDFViewer.
pdf_show = pdf.ShowPdf()
# Adding pdf location and width and height.
V_pdf = pdf_show.pdf_view(master=frame_r,
pdf_location=(doc_list[doc_counter]),
width=90, height=100)
V_pdf.pack(side=tk.LEFT)
#button skip
def skip_callback():
global doc_counter
doc_counter = doc_counter +1
# Here I want to display the next PDF!!
V_pdf = pdf_show.pdf_view(master=frame_r,
pdf_location=(doc_list[doc_counter]),
width=90, height=100)
V_pdf.pack()
print(doc_counter)
button_skip = Button(root, text='skip', command= skip_callback)
button_skip.pack(fill=tk.X, pady=0)
root.mainloop()
When I hit the 'skip' button, the next pdf from the 'parent_path' is supposed to appear where the initial one was displayed.
Thanks for your help!
Flo
I have looked into tkPDFViewer's code and the ShowPdf class has not been designed to load a new document after the initial one.
The reason why the second file is displayed next to the first one is that pdf_show.pdf_view() creates a new Frame which is then packed on the right of the previous one. So you need to destroy the old one first with
pdf_show.frame.destroy()
Then, I have noticed that instead of displaying the second document when clicking on the skip button, what is displayed is a concatenation of both documents. This can be solved by clearing the image list of pdf_show
pdf_show.img_object_li.clear()
Here is the full code:
import tkinter as tk
import glob
from tkPDFViewer import tkPDFViewer as pdf
parent_path = '/home/juliette/Documents'
doc_list = glob.glob((parent_path + "*//*.pdf"))
doc_counter = 0
root = tk.Tk()
root.title('Training Data Creator')
root.geometry("1000x1000")
frame_r = tk.Frame(root, relief=tk.RAISED, borderwidth=1)
frame_r.pack(fill=tk.BOTH, expand=True, side=tk.LEFT)
# creating object of ShowPdf from tkPDFViewer.
pdf_show = pdf.ShowPdf()
# Adding pdf location and width and height.
V_pdf = pdf_show.pdf_view(master=frame_r,
pdf_location=(doc_list[doc_counter]),
width=90, height=100)
V_pdf.pack(side=tk.LEFT)
#button skip
def skip_callback():
global doc_counter
doc_counter = doc_counter +1
# Reset view:
pdf_show.frame.destroy()
pdf_show.img_object_li.clear()
# Display new pdf
V_pdf = pdf_show.pdf_view(master=frame_r,
pdf_location=(doc_list[doc_counter]),
width=90, height=100)
V_pdf.pack(side=tk.LEFT)
print(doc_counter)
button_skip = tk.Button(root, text='skip', command= skip_callback)
button_skip.pack(fill=tk.X, pady=0)
root.mainloop()
By the way, you are importing tkinter twice:
from tkinter import *
import tkinter as tk
so I advise you to choose one of the ways to import it and stick with it, rather than mixing both. Furthermore, I suggest you to use import tkinter as tk, this way you will not crowd the namespace with a large amount of unknown names that can lead to naming conflicts (e.g. if you import Image from PIL and then do from tkinter import *, Image will be the tkinter class, not the PIL one).
Related
I created a Text field in Tkinter, new items added next to the text field and I can't get the bottom line with grid. window does not open when grid is added. What is the solution?
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import tkinter as tk
pencere = tk.Tk()
frame = tk.Frame(pencere)
frame.pack()
scrol = tk.Scrollbar(frame)
scrol.pack(side=tk.RIGHT, fill=tk.Y)
text = "Good bye"
text = tk.Text(frame,width=70, height=0)
text.insert(tk.INSERT, text)
text.config(yscrollcommand=scrol.set)
scrol.config(command=text.yview)
text.pack(side=tk.LEFT, fill=tk.Y)
buton = tk.Button(frame, text='close', command=pencere.destroy)
buton.pack()
text.grid(row=1, column=1)
pencere.mainloop()
You cannot use pack and grid methods for the widgets which have the same master. In this example, your frame, text and scrol are already adjusted by pack therefore tkinter does not allow to adjust text or an another element with grid as long as you don't create another frame for the element that you want to adjust with grid.
So, in my opinion there are two options:
If you still want to use grid, you should adjust whole widgets(frame, text etc.) with grid.
If you want to use grid but you don't want to change widgets which are alredy adjusted by pack, create a new frame for your text.
Dear Zolo Baba the bar cannot appear because you are still using "pack" and "grid" together. Because of the reasons that I mentioned above, we can use only one layout management method for the master. I edited your code with using only "grid" method.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import tkinter as tk
pencere = tk.Tk()
frame = tk.Frame(pencere)
frame.grid()
scrol = tk.Scrollbar(frame, orient = tk.VERTICAL)
text = tk.Text(frame,width=70, height=5)
text.insert(tk.INSERT, "###\n\n\nNote\n\n\n###")
text.config(yscrollcommand=scrol.set)
text.grid(sticky="W")
scrol.config(command=text.yview)
scrol.grid(row = 0, column=2, sticky="NS")
buton = tk.Button(frame, text='close', command=pencere.destroy)
buton.grid(row = 0, column = 1)
pencere.mainloop()
I am trying to bind a photo to the list box, but the photo does not appear.
I tried to take a specific photo path here. with the same code above (in the choosePhoto) and it worked. For some reason when in the code inside the function and is binding the function to the listBox, the photo does not appear.
My code:
from tkinter import *
from PIL import ImageTk, Image
from os import *
def openPath(path,listBox):
try:
path2=str(path)
list1= listdir(path2)
listBox.delete(0,END)
for i in range(len(list1)):
listBox.insert(i,list1[i])
except:
print("file does not exist")
def choosePhoto(event):
path=str(textFolder.get())+"\\"+str(listBoxPath.get(ACTIVE))
image1=ImageTk.PhotoImage(Image.open(path))
lbl.configure(image=image1)
print(path)
root = Tk()
root.geometry("450x600")
root.title("project image proccesor")
frame1=Frame(root,width=250,height=100)
frame1.pack(side=LEFT,fill=BOTH)
frame4=Frame(root,width=250,height=100)
frame4.pack(side=RIGHT,fill=BOTH)
lblFolder=Label(frame1,text="Enter folder path:")
lblFolder.grid(row=0,column=0)
textFolder=Entry(frame1,insertwidth=4)
textFolder.grid(rowspan=1,column=0)
listBoxPath=Listbox(frame1)
listBoxPath.grid(row=2)
bChoose=Button(frame1,text="Choose",command=lambda: openPath(textFolder.get(),listBoxPath)).grid(row=1,column=1)
lbl=Label(frame4, text="waiting for photo")
listBoxPath.bind('<<ListboxSelect>>', choosePhoto)
root.mainloop()
There are 3 issues I can see here in your code.
1st. You need to define image1 as a global because this image is currently a local variable in the function and once the function completes the images is deleted unless you define it in the global namespace.
2nd. Your label that is used for displaying the images has not yet been placed on the screen. You need to use some geometry manager (probably grid()) in this case to display the image.
3rd. You are currently using ACTIVE in your selection on the list box. This will results in you selecting what was active prior to you clicking instead of what you just clicked on.
Change this:
list_box_path.get(ACTIVE)
to this:
list_box_path.get(list_box_path.curselection())
I have cleaned up your code a bit to more closely fit the PEP8 standard and added some minor changes and reduced section of code that were not needed.
import tkinter as tk
from PIL import ImageTk, Image
from os import listdir
def open_path(path):
try:
list1 = listdir(path)
list_box_path.delete(0, "end")
for i in range(len(list1)):
list_box_path.insert(i, list1[i])
except:
print("file does not exist")
def choose_photo(event):
global image1
path = Image.open("{}\\{}".format(text_folder.get(), list_box_path.get(list_box_path.curselection())))
image1 = ImageTk.PhotoImage(path)
lbl.configure(image=image1)
root = tk.Tk()
root.geometry("450x600")
root.title("project image processor")
frame1 = tk.Frame(root, width=250, height=100)
frame4 = tk.Frame(root, width=250, height=100)
lbl_folder = tk.Label(frame1, text="Enter folder path:")
text_folder = tk.Entry(frame1, insertwidth=4)
list_box_path = tk.Listbox(frame1)
b_choose = tk.Button(frame1, text="Choose", command=lambda: open_path(text_folder.get()))
lbl = tk.Label(frame4, text="waiting for photo")
frame1.pack(side="left", fill="both")
frame4.pack(side="right", fill="both")
lbl_folder.grid(row=0, column=0)
text_folder.grid(rowspan=1, column=0)
list_box_path.grid(row=2)
b_choose.grid(row=1, column=1)
lbl.grid(row=0, column=0)
list_box_path.bind('<<ListboxSelect>>', choose_photo)
root.mainloop()
I'm trying to make a link slicer that automatically slice and copy the sliced output of the link right after I pasted it on txt = Entry(window,width=50) without clicking a button.
When I paste a link something like this: http://url.io/s/1234abcd/?s=https%3A%2F%2Fexample.com%2Fsome_contens%2F then it will become: example.com/some_contents/
Right now the code below is what I use, but I still have to click the "slice" button before "slicing" and "copying" happens.
from tkinter import *
from urllib import parse
from tkinter import Tk
window = Tk()
window.title("Link Slicer")
window.geometry('344x50')
lbl = Label(window, text="Link")
lbl.grid(column=0, row=1)
txt = Entry(window,width=50)
txt.grid(column=0, row=0)
def clicked():
sliced = txt.get()
sliced = parse.unquote(sliced)
lbl.configure(text= sliced[36:])
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append(sliced[36:])
r.update()
btn = Button(window, text="Slice", command=clicked)
btn.grid(column=1, row=0)
window.mainloop()
I made this code below to try to automate "slicing" and "copying" right after I paste the link in the txt = Entry(window,width=50) without clicking a button (which doesn't work):
from tkinter import *
from urllib import parse
from tkinter import Tk
window = Tk()
window.title("Link Slicer")
window.geometry('344x50')
lbl = Label(window, text="Link")
lbl.grid(column=0, row=1)
txt = Entry(window,width=50)
txt.grid(column=0, row=0)
sliced = txt.get() // 1. automatically get what's in txt = Entry(window,width=50)
sliced = parse.unquote(sliced) // 2. auto-slice
r = Tk() }
r.withdraw() }
r.clipboard_clear() } // 3. auto-copy
r.clipboard_append(sliced[36:]). }
r.update() }
window.mainloop()
Since you always want to operate on the clipboard contents there's no need to even paste the clip into the Entry. You can just grab the clip, transform it, and put it back into the clipboard. I've included an Entry to display the result, but that's not really necessary.
import tkinter as tk
from urllib import parse
root = tk.Tk()
root.title("Link Slicer")
root.geometry('344x50')
root.attributes("-topmost", True)
def clicked():
clip = root.clipboard_get()
clip = parse.unquote(clip)[36:]
root.clipboard_clear()
root.clipboard_append(clip)
entry_text.set(clip)
entry_text = tk.StringVar()
txt = tk.Entry(root, textvariable=entry_text, width=50)
txt.pack()
btn = tk.Button(root, text="Slice", command=clicked)
btn.pack()
root.mainloop()
When I copy the sample URL from your question and click the Slice button I get this result displayed in the Entry and copied to the clipboard. Note that I did not paste the URL into the Entry, I just clicked the button.
example.com/some_contens/
I've included root.attributes("-topmost", True) to force the window to stay above other windows, because I think that's convenient for a tool like this.
As I mentioned in the comments, you should not call Tk() more than once because each call starts a new instance of the Tcl interpreter, which can lead to confusing behaviour (and it's a waste of resources).
I'm using python 3.4 and tkinter. I have a notebook with two pages. I have a listbox that needs to be on both pages.
I'm wondering if I can setup the listbox once and use it on both pages, or if I need to setup a separate listbox on each page and manage both as the list box in one page changes?
You cannot pack/grid/place the listbox inside two different frames simultaneously.
However, you can re-pack/grid/place the listbox each time the notebook tab changes. To do so, I used the <<NotebookTabChanged>> event which is triggered each time the notebook tab changes:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
notebook = ttk.Notebook(root)
notebook.pack()
frame1 = tk.Frame(notebook, bg='red', width=400, height=400)
frame1.pack_propagate(False)
frame2 = tk.Frame(notebook, bg='blue', width=400, height=400)
frame2.pack_propagate(False)
notebook.add(frame1, text='frame 1')
notebook.add(frame2, text='frame 2')
var = tk.StringVar(root, 'a b c d e f g')
listbox = tk.Listbox(notebook, listvariable=var)
def display_listbox(event):
tab = notebook.tabs()[notebook.index('current')]
listbox.pack(in_=tab)
notebook.bind('<<NotebookTabChanged>>', display_listbox)
root.mainloop()
Explanations about display_listbox:
notebook.tabs() returns (frame1, frame2) (i.e the tuple of the tabs)
notebook.index('current') returns the index of the currently visible tab
the in_ option can be used to specify the widget in which we want to pack the listbox (it also works with grid and place)
Sharing the same listvariable between the two list boxes is sufficient to keep them synchronized.
import tkinter as tk
from tkinter import ttk
rootWin = tk.Tk()
rootWin.title('Talk Like A Pirate')
listvar = tk.StringVar()
#create a notebook
pirateBook = ttk.Notebook(rootWin)
#create a tab
tab1 = tk.Frame(pirateBook)
#create a listbox in the tab
listbx1 = tk.Listbox(tab1,
listvariable=listvar,
height=21,
width=56,
selectmode='single'
)
listbx1.pack()
#create another tab
tab2 = tk.Frame(pirateBook)
#create a listbox in the second tab
listbx2 = tk.Listbox(tab2,
listvariable=listvar,
height=21,
width=56,
selectmode='single'
)
listbx2.pack()
#add the tabs to the notebook
pirateBook.add(tab1, text="Tab 1")
pirateBook.add(tab2, text="Tab 2")
#pack the notebook
pirateBook.pack()
#you can access the listbox through the listvariable, but that takes
# a list as the argument so you'll need to build a list first:
ls=list() #or you could just ls = []
#build a list for the list box
ls.append("Arr, matey!")
ls.append("Dead men tell no tales!")
ls.append("Heave ho, me hearties!")
ls.append("I'll cleave ye t'yer brisket a'fore sendin' ye to Davey Jones Locker!")
#then just set the list variable with the list
listvar.set(ls)
#or you can manipulate the data using Listbox()'s data manipulation
# methods, such as .insert()
listbx1.insert(1, "Shiver me timbers!")
listbx2.insert(3, "Yo ho ho!")
listbx1.insert(5, "Ye scurvy dog!")
listbx2.insert('end', "Ye'll be walking the plank, Sharkbait!")
#you'll see this if you run this program from the command line
print(listbx1.get(5))
rootWin.mainloop()
You would do this a little bit differently if you had your tabs in their own classes, making the common variable global, for instance, but the same idea still applies: sharing a common listvariable.
I'm trying to write a script that will allow the user to select a folder with images and then save the coordinates of the user's clicks on each image. To do this I would like to display each image file on a Tkinter canvas, save click locations, then when the canvas is closed, open the next image.
I can get this to work for a single image with the code below (adapted from this question). I was hoping the for File in imgs loop would keep opening the next image, but it does not. I suspect I need a on_closing function to tell Tkinter to open another image.
What's the proper way to have Tkinter open the next image after closing an image?
from Tkinter import *
from tkFileDialog import askopenfilenames, askopenfilename, askdirectory
from PIL import Image, ImageTk
import cv2
import numpy as np
import os
if __name__ == "__main__":
root = Tk()
#setting up a tkinter canvas with scrollbars
frame = Frame(width=1920, height=1080, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, bd=0, xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)
canvas.config(width=1920, height=1080)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
# Function to be called when mouse is clicked
def save_coords(event):
coords.append([event.x, event.y])
# def on_closing():
# Open the next image file
# Create empty list for coordinate arrays to be appended to
coords = []
# Choose multiple images
img_dir = askdirectory(parent=root, initialdir="D:/Temp/", title='Choose folder')
os.chdir(img_dir)
imgs = os.listdir(img_dir)
#imgs = askopenfilenames(parent=root, initialdir="D:/Temp/cvCal/", title='Choose images')
for File in imgs:
img = ImageTk.PhotoImage(Image.open(File).resize((1280,720), Image.ANTIALIAS))
canvas.create_image(0,0,image=img,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
canvas.bind("<Button 1>",save_coords)
# on_closing()...
root.mainloop()
It would be a lot easier if you can use a Label instead:
Also, note there is no reason to use ImageTk since loading an image from a file is built into tkinter as Tkinter.PhotoImage.
Also, I converted your wildcard import to a normal import; wildcard imports are messy and against PEP8.
Lastly, I don't know what you mean with 'on closing', so I added a button to advance to the next image. This will throw a StopIteration error on the last image that you will need to handle.
import Tkinter as tk
from tkFileDialog import askdirectory
import os
# Create empty list for coordinate arrays to be appended to
coords = []
# Function to be called when mouse is clicked
def save_coords(event):
click_loc = [event.x, event.y]
print "you clicked on", click_loc
coords.append(click_loc)
# Function to load the next image into the Label
def next_img():
img_label.img = tk.PhotoImage(file=next(imgs))
img_label.config(image=img_label.img)
root = tk.Tk()
# Choose multiple images
img_dir = askdirectory(parent=root, initialdir="D:/Temp/", title='Choose folder')
os.chdir(img_dir)
imgs = iter(os.listdir(img_dir))
img_label = tk.Label(root)
img_label.pack()
img_label.bind("<Button-1>",save_coords)
btn = tk.Button(root, text='Next image', command=next_img)
btn.pack()
next_img() # load first image
root.mainloop()
print coords