2016-01-03 17 views
12

Sto scrivendo un programma in Python con un'interfaccia utente di Tkinter. Voglio avere una piccola finestra senza barra del titolo. Questa finestra deve ricevere l'input da tastiera. Non sono schizzinoso se questo è sotto forma di un widget Entry o semplicemente vincolante per KeyPress. overrideredirect(True) è in genere il modo in cui la barra del titolo è disabilitata. Sfortunatamente, (eccetto in Windows), questo sembra impedire la ricezione di molti eventi. Ho scritto questo codice per illustrare il problema:L'overrideredirect di Tkinter impedisce determinati eventi in Mac e Linux

#!/usr/bin/env python 
from __future__ import print_function 
import Tkinter 

class AppWindow(Tkinter.Tk): 
    def __init__(self, *args, **kwargs): 
     Tkinter.Tk.__init__(self, *args, **kwargs) 
     self.overrideredirect(True) 
     self.geometry("400x25+100+300") 

     titleBar = Tkinter.Frame(self) 
     titleBar.pack(expand = 1, fill = Tkinter.BOTH) 

     closeButton = Tkinter.Label(titleBar, text = "x") 
     closeButton.pack(side = Tkinter.RIGHT) 
     closeButton.bind("<Button-1>", lambda event: self.destroy()) 

     self.bind("<KeyPress>", lambda event: print("<KeyPress %s>" % event.char)) 
     self.bind("<Button-1>", lambda event: print("<Button-1>")) 
     self.bind("<Enter>", lambda event: print("<Enter>")) 
     self.bind("<Leave>", lambda event: print("<Leave>")) 
     self.bind("<FocusIn>", lambda event: print("<FocusIn>")) 
     self.bind("<FocusOut>", lambda event: print("<FocusOut>")) 

if __name__ == "__main__": 
    app = AppWindow() 
    app.mainloop() 

Questo crea una piccola finestra (senza barra del titolo) che stampa il nome di eventi comuni quando li riceve. Ho eseguito questo script su Windows 7, Mac OSX (El Capitan) e Ubuntu 14.04.1. Ho eseguito solo Ubuntu su una macchina virtuale (VMWare).

  • In Windows, sembra funzionare come previsto. Possono essere ricevuti tutti gli eventi che il mio codice verifica.

  • In Ubuntu, la finestra Tkinter riceve <Enter>, <Leave>, e <Button-1> eventi come previsto, ma <KeyPress>, <FocusIn> e <FocusOut> non sono mai ricevuto. Infatti, anche dopo aver fatto clic sulla finestra, l'ultima finestra con lo stato attivo continua a ricevere le pressioni dei tasti.

  • in OSX, finestra Tkinter riceve <Button-1> eventi come previsto, ma <KeyPress>, <FocusIn> e <FocusOut> non sono mai ricevuti. L'ultima finestra con focus non continua a ricevere le pressioni dei tasti come in Ubuntu. Gli eventi <Enter> e <Leave> si comportano in modo un po 'strano. L'evento <Enter> non viene ricevuto finché non si fa clic sulla finestra. Quindi, una volta che si verifica l'evento <Leave>, è necessario fare di nuovo clic sulla finestra per ricevere un altro evento <Enter>.

ho anche provato self.focus_force() appena prima della fine della funzione __init__. Ciò causa che la finestra riceve un evento <FocusIn> all'avvio del programma, ma non ulteriori <KeyPress>, <FocusIn> o <FocusOut> eventi non si ricevono mai.

In definitiva, la mia domanda è questa: esiste un modo per nascondere la barra del titolo ma continuare a ricevere input da tastiera in OSX e Linux?


Sono a conoscenza di alcune altre domande relative a questo stesso problema. In queste tre domande:

La risposta accettata è quella di utilizzare self.attributes('-fullscreen', True), che non funziona per me come io voglio una minuscola finestrella, non un applicazione a schermo intero.

C'è un'altra domanda: Tkinter overrideredirect no longer receiving event bindings. Questo sembra molto vicino alla mia domanda, ma ha fornito meno dettagli e non ha risposta.


Aggiornamento: mi hanno cercato di indagare il meccanismo alla base del mio problema. So che Tkinter è un wrapper di Tcl/Tk, quindi ho pensato di provare a riscrivere il mio codice in Tcl. Io non so davvero Tcl, ma penso che sono riuscito a (più o meno) tradurre il mio Python:

#!/usr/bin/env wish 
wm overrideredirect . True 
wm geometry . "400x25+100+300" 
bind . <KeyPress> {puts "<KeyPress %K>"} 
bind . <Button-1> {puts "<Button-1>"} 
bind . <Enter> {puts "<Enter>"} 
bind . <Leave> {puts "<Leave>"} 
bind . <FocusIn> {puts "<FocusIn>"} 
bind . <FocusOut> {puts "<FocusOut>"} 

ho provato il programma risultante in Windows e Mac OSX. In Windows ho ricevuto gli eventi <KeyPress>, ma in OSX non l'ho fatto. Senza la riga wm overrideredirect . True, OSX riceve gli eventi <KeyPress>. Quindi sembra che questo problema non sia con Python, ma con Tcl/Tk.

+0

Disabilitare 'mainloop' e' come accedervi di nuovo? 'Binding (key listen) funziona con gli elementi secondari. Hai ottenuto un'applicazione 'fantasma' se' ascoltatore' è 'congelato'. – dsgdfg

+0

A trucchi: crea il tuo pulsante [X] come mainloop (quindi 'deiconify()')! – dsgdfg

risposta

2

Ho presentato una segnalazione di errore a Tk per questa situazione.

È possibile utilizzare il programma devilspie per rimuovere le decorazioni dalla finestra. Utilizzare il comando wm title . myname per assegnare alla finestra un nome specifico e utilizzare tale nome nel seguente frammento di configurazione devilspie. Rimuovere il comando dal programma.

Ho provato questo (come un programma Tk), e la finestra non decorata riceverà ancora i binding di tasti da premere & ecc.

Si noti che devilspie è scritto come processo daemon e rimane attivo. Il demone può essere ucciso dopo che è stato avviato e la finestra modificata non sarà ancora attiva. Oppure può essere lasciato in esecuzione, e ogni volta che la finestra viene attivata, verrà applicata la configurazione devilspie.

(if (is (application_name) "t.tcl") 
    (begin (undecorate))) 
+0

Err ... questo è per Linux. Non so se c'è un programma equivalente per il Mac. –

+0

Grazie, funziona perfettamente per me (Archlinux con LXDE). –

+0

una finestra con overrideredircct è deliberatamente impedito di prendere fuoco, e quindi non può ottenere gli eventi chiave: http://core.tcl.tk/tk/artifact/7892c68f49012d2d71222ae0e312a1e7dc69a801?txt=1&ln=51-64 Quella linea e commentare è antico e potrebbe essere possibile rimuovere. Non sono sicuro. – Hugge

Problemi correlati