python - Making a pop-up keyboard in Tkinter with Toplevel -


i have small module pops toplevel widget when entry widget gains focus. toplevel window keyboard, expects button clicks trigger method inserts button click entry widget. toplevel should destroyed on 2 conditions: 1) user presses key on actual keyboard, 2) parent of entry widget moved or resized.

everything works, except 1 bug: if user clicks on toplevel, becomes active, , if 1 of destroy events occur, unintended results (like popup coming when entry gets focus again).

my thought if can make entry retain focus throughout process, work, haven't found way make happen.

here's example, it's stripped down can make while retaining structure of module. note: python 2.7

from tkinter import *  class top(toplevel):     def __init__(self, attach):         toplevel.__init__(self)         self.attach = attach         button(self, text='button a', command=self.callback).pack()          self.bind('<key>', self.destroypopup)      def callback(self):         self.attach.insert(end, 'a')      def destroypopup(self, event):         self.destroy()  class entrybox(frame):     def __init__(self, parent, *args, **kwargs):         frame.__init__(self, parent)         self.parent = parent          self.entry = entry(self, *args, **kwargs)         self.entry.pack()          self.entry.bind('<focusin>', self.createpopup)         self.entry.bind('<key>', self.destroypopup)         self.parent.bind('<configure>', self.destroypopup)      def createpopup(self, event):         self.top = top(self.entry)      def destroypopup(self, event):         try:             self.top.destroy()         except attributeerror:             pass  root = tk()  e1 = entrybox(root).pack()  root.mainloop() 

so, there kind of never_get_focus() method haven't found can apply toplevel widget, or approaching problem wrong way, or what? appreciated.

edit: found band-aid type solution seems work, feel there's still better way handle haven't found yet.

here's i've added frame subclass popup methods:

def createpopup(self, event):     try:                           #when focus moves in entry widget,         self.top.destroy()         #try destroy toplevel         del self.top               #and remove reference     except attributeerror:         #unless doesn't exist,         self.top = top(self.entry) #then, create  def destroypopup(self, event):     try:         self.top.destroy()         del self.top     except attributeerror:         pass 

i'm adding bounty because want see if there's another, cleaner way of doing this. steps want are:

  1. focus moves entry widget
  2. a popup toplevel created (this keyboard)
  3. the toplevel destroyed when a) key press event occurs actual keyboard, b) focus moves out of entry widget widget or off gui, c) main window moved
  4. this process repeatable if focus moves entry widget later

you can use state machine handle behavior describe. state machines quite common implement behaviors in graphical user interfaces. here brief example give idea of might like.

first design fsm, here simple 1 perform want (skip configure part sake of brevity).

fsm picture

for implementation, might pick existing library, build own framework, or go old nested if . following quick , dirty implementation.

adapt subscription create state , redirect event fsm:

    self.state = "idle"     self.entry.bind('<focusin>', lambda e:self.fsm("focus_entry"))     self.entry.bind('<focusout>', lambda e:self.fsm("focus_out_entry"))     self.entry.bind('<key>', lambda e:self.fsm("key_entry"))     self.parent.bind('<configure>', lambda e:self.fsm("configure_parent")) 

select combination of state / event want address , put appropriate actions. might discover trapped in state , adapt fsm accordingly.

def fsm(self, event):     old_state = self.state #only logging     if self.state == "idle":         if event == "focus_entry":             self.createpopup()             self.state = "virtualkeyboard"     elif self.state == "virtualkeyboard":         if event == "focus_entry":             self.destroypopup()             self.state = "typing"         if event == "focus_out_entry":             self.destroypopup()             self.state = "idle"         elif event == "key_entry":             self.destroypopup()             self.state = "typing"     elif self.state == "typing":         if event == "focus_out_entry":             self.state = "idle"     print "{} --{}--> {}".format(old_state, event, self.state) 

Comments

Popular posts from this blog

PHPMotion implementation - URL based videos (Hosted on separate location) -

c# - Unity IoC Lifetime per HttpRequest for UserStore -

I am trying to solve the error message 'incompatible ranks 0 and 1 in assignment' in a fortran 95 program. -