可调用的Tkinter Toplevel弹出对话框

时间:2017-03-26 21:55:11

标签: python tkinter

我是python和tkinter的新手。我一直在努力提高我的技能,所以我可以开始编程我的覆盆子Pi。我当前的问题让我感到难过:当我点击Entry字段时,我想调出一个对话框(屏幕键盘)。

我已经尝试了几种方法来实现这一目标。我可以让键盘在开头显示一次或者根本不显示。但不是点击事件。我知道键盘类正在使用它,所以它必须与我如何调用它(popout.py的第29行)。根据我尝试实现的方式,我收到两条错误消息之一:

Exception in Tkinter callback
Traceback (most recent call last):
  File "E:\Programs\Python3\lib\tkinter\__init__.py", line 1533, in __call__
    return self.func(*args)
TypeError: keyboard() takes from 1 to 3 positional arguments but 4 were given

Exception in Tkinter callback
Traceback (most recent call last):
  File "E:\Programs\Python3\lib\tkinter\__init__.py", line 1533, in __call__
    return self.func(*args)
TypeError: 'Keyboard' object is not callable

我真的不明白为了获得这些例外会发生什么。以下是我的代码:

Popup.py

import tkinter as tk
import keyboard as k
# 
from time import sleep
# needs Python25 or higher
from functools import partial

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()
        #variable to update counter text
        self.num = tk.StringVar()
        self.create_widgets()

    def create_widgets(self):
        # button to start counting
        self.startbtn = tk.Button(self)
        self.startbtn["text"] = "Start counting\n(Click)"
        self.startbtn["command"] = self.counting
        self.startbtn.pack(side="top")

        #Label to view counter
        self.counter = tk.Label(self, textvariable=self.num)
        self.counter.pack()

        #Entry box
        self.e = tk.StringVar()
        self.entry = tk.Entry(self, textvariable=self.e)
        #launch_kbd = partial(self.keyboard, "MyKeyboard", self.e)
        #self.entry.bind("<Button-1>", launch_kbd)
        self.entry.bind("<Button-1>", k.Keyboard(self, "MyKeyboard", self.e))
        self.entry.pack()

        #Exit
        self.quit = tk.Button(self, text="Quit", fg="red", command=root.destroy)
        self.quit.pack(side="bottom")

    #count for 10 seconds
    def counting(self):
        self.startbtn["state"] = "disabled"
        root.update()
        #loop from 1-10
        for x in range(1,11):
            self.num.set(str(x)) #update the label's variable
            root.update()    #important to make changes visible
            sleep(1)    #sleep for 1 second
        #Clear text when done
        self.num.set("")
        self.startbtn["state"] = "enabled"
        root.update()
    #def keyboard(self, title=None, target=None):
    #    k.Keyboard(self, title, target)
'''
This starts the root window
'''
root = tk.Tk()
root.title("myGUI")
root.geometry("200x100")

app = Application(master=root)
app.mainloop()

keyboard.py

'''
# Built with examples from:
- http://effbot.org/tkinterbook/tkinter-dialog-windows.htm
- https://www.daniweb.com/programming/software-development/threads/300867/on-screen-keyboards-in-tkinter

# Desc: Creates a pop-up on-screen Keyboard for text entry. 
        Writes entry to Target upon pressing Enter.
'''
# use tkinter for GUI application
import tkinter as tk

# used for button click
from functools import partial

class Keyboard(tk.Toplevel):
    def __init__(self, parent, title=None, target=None):

        tk.Toplevel.__init__(self, parent, bg='black')
        # associate this window with a parent window 
        self.transient(parent)

        if title:
            self.title(title)
        if target:
            self.target = target
            self.init_txt = target.get()
        self.parent = parent

        body = tk.Frame(self)
        self.body(body)
        body.pack(padx=5, pady=5)

        self.buttonbox()

        #make the dialog modal
        self.grab_set()

        # safely handle WM_DELETE_WINDOW event
        self.protocol("WM_DELETE_WINDOW", self.close)

        # position the dialog relative to the parent window
        self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50))

        # Hold captive control until window is closed
        # prevent parent from accepting input while window is open
        self.wait_window(self)

    # END Init

    def body(self, master):
        #Text input at the top
        self.txt = tk.StringVar()
        self.txt.set(self.init_txt)
        self.tx = tk.Entry(self, bg='black', fg='white', insertbackground='gray', insertwidth=2, textvariable=self.txt)
        self.tx.focus_set()
        self.tx.pack(fill="x", padx=5, pady=5)

    def buttonbox(self):
        # create a frame for the keypad buttons
        bf = tk.Frame(self)
        bf.pack()

        # button layout
        btn_list = [
        'Q',  'W',  'E',  'R',  'T', 'Y', 'U', 'I', 'O', 'P',
        'A',  'S',  'D',  'F',  'G', 'H', 'J', 'K', 'L', ';',
        'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '?',
        'Num', '+', '-', ' ', ' ', ' ', '<-', '->', 'BKS', 'ETR', ]

        # create and position all buttons with a for-loop
        # r, c used for row, column grid values
        r = 1
        c = 0
        n = 0
        # list(range()) needed for Python3
        btn = list(range(len(btn_list)))

        for label in btn_list:
            # partial takes care of function and argument
            cmd = partial(self.click, label)
            # create the button (and style it)
            btn[n] = tk.Button(bf, text=label, width=5, height=3, command=cmd, bg='#333333', fg='white', activebackground='#666666', activeforeground='white')
            # position the button
            btn[n].grid(row=r, column=c)
            # increment button index
            n += 1
            # update row/column position
            c += 1
            if c > 9:
                c = 0
                r += 1

    def click(self, btn):
        if btn == 'Num':
            # Switch to number pad
            pass
        elif btn == '<-':
            # Move cursor to the left 1 character
            self.tx.icursor(self.tx.index('insert')-1)
        elif btn == '->':
            # Move cursor to the right 1 character
            #icursor(index)
            self.tx.icursor(self.tx.index('insert')+1)
        elif btn == 'BKS':
            # truncate entry by 1 character
            self.tx.delete(self.tx.index('insert')-1)
        elif btn == 'ETR':
            # return entered value to the input
            self.apply()
            self.close()
        else:
            # DEFAULT: append entry with btn
            self.tx.insert('insert', btn)

    def close(self, event=None):

        # put focus back to the parent window
        self.parent.focus_set()
        self.destroy()

    def apply(self):

        self.target.set(self.tx.get())

# if called directly
if __name__ == "__main__":
    root = tk.Tk()
    t = tk.StringVar()
    k = Keyboard(root, "MyKeyboard", target=t)
    print(t.get())

1 个答案:

答案 0 :(得分:0)

使用Button-1函数将参数传递给Popup.py事件函数。

self.entry.bind("<Button-1>", k.Keyboard(self, "MyKeyboard", self.e))的第33行,更改:

self.entry.bind("<Button-1>", lambda event: k.Keyboard(self, "MyKeyboard", self.e))

为:

self.startbtn["state"] = "enabled"

此外,counting方法下的self.startbtn["state"] = "normal"应为Message