2016-01-03 15 views
12

Piszę program w Pythonie z interfejsem użytkownika Tkinter. Chcę mieć małe okno bez paska tytułu. To okno musi odbierać dane z klawiatury. Nie jestem wybredny, czy ma to postać widżetu wpisowego, czy tylko wiążącego się z KeyPress. overrideredirect(True) to zazwyczaj sposób wyłączania paska tytułu. Niestety, (z wyjątkiem Windows) wydaje się, że nie można odbierać wielu zdarzeń. Napisałem ten kod do zilustrowania problemu:Overrideredirect Tkintera zapobiega niektórym zdarzeniom w systemie Mac i 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() 

Stwarza to małe okienko (bez paska tytułowego), który drukuje nazwę wspólnych imprez, gdy otrzyma je. Uruchomiłem ten skrypt na Windows 7, Mac OSX (El Capitan) i Ubuntu 14.04.1. Uruchomiłem tylko Ubuntu na wirtualnej maszynie (VMWare).

  • W systemie Windows wygląda na to, że działa poprawnie. Wszystkie zdarzenia, na które mój kod testuje, mogą zostać odebrane.

  • W Ubuntu, okna Tkinter odbiera <Enter>, <Leave> i <Button-1> wydarzenia jak oczekiwano, ale <KeyPress>, <FocusIn> i <FocusOut> nigdy nie są odbierane. W rzeczywistości, nawet po kliknięciu okna, ostatnie okno z fokusem kontynuuje otrzymywanie naciśnięć klawiszy.

  • W OSX, okna Tkinter odbiera <Button-1> wydarzenia jak oczekiwano, ale <KeyPress>, <FocusIn> i <FocusOut> nigdy nie są odbierane. Ostatnie okno z fokusem nie kontynuuje odbierania naciśnięć klawiszy, tak jak w Ubuntu. Zdarzenia <Enter> izachowują się trochę dziwnie. Zdarzenie <Enter> nie zostanie odebrane, dopóki okno nie zostanie kliknięte. Następnie, po wystąpieniu zdarzenia <Leave>, okno musi zostać ponownie kliknięte, aby odebrać kolejne zdarzenie <Enter>.

Próbowałem również self.focus_force() tuż przed końcem funkcji __init__. Powoduje to, że okno odbiera zdarzenie <FocusIn> podczas uruchamiania programu, ale żadne dalsze zdarzenia nie są nigdy odbierane.

W końcu moje pytanie brzmi następująco: czy istnieje sposób na ukrycie paska tytułu, ale nadal otrzymujesz dane z klawiatury w systemach OSX i Linux?


Jestem świadomy kilku innych pytań dotyczących tego samego problemu. W tych trzech pytań:

Zaakceptowanych odpowiedź jest użycie self.attributes('-fullscreen', True), które nie będą pracować dla mnie jak chcę to malutkie okna, a nie aplikacja pełnoekranowa.

Jest jeszcze jedno pytanie: Tkinter overrideredirect no longer receiving event bindings. Wydaje się to bardzo bliskie mojemu pytaniu, ale zawiera mniej szczegółów i nie ma odpowiedzi.


Aktualizacja: I zostały próby zbadania mechanizmu leżącego u podstaw mojego problemu. Wiem, że Tkinter jest opakowaniem wokół Tcl/Tk, więc pomyślałem, że spróbuję przepisać mój kod w Tcl. I naprawdę nie wiem, Tcl, ale myślę, że udało mi się (mniej lub bardziej) przetłumaczyć moją 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>"} 

Próbowałem wynikowy program w systemie Windows i Mac OSX. W systemie Windows otrzymałem <KeyPress> zdarzeń, ale w systemie OSX nie. Bez linii wm overrideredirect . True system OSX odbiera zdarzenia <KeyPress>. Dlatego wygląda na to, że ten problem nie występuje w Pythonie, ale w Tcl/Tk.

+0

Wyłączyć 'mainloop' i' jak uzyskać do niego dostęp ponownie? 'Binding (key listen) działa z elementami podrzędnymi. Zyskałeś aplikację 'ghost', jeśli' listener' jest 'zamrożony'. – dsgdfg

+0

Sztuczki: Ustaw swój przycisk [X] jako mainloop (a więc 'deiconify()')! – dsgdfg

Odpowiedz

2

Złożyłem zgłoszenie błędu do TK dla tej sytuacji.

Możesz użyć programu devilspie, aby usunąć ozdoby z okna. Użyj komendy wm title . myname, aby nadać plikowi określoną nazwę i użyć tej nazwy w poniższym fragmencie konfiguracji devilspie. Usuń komendę overrideredirect ze swojego programu.

Przetestowałem to (jako program TK), a nieodnowione okno nadal będzie otrzymywało powiązania z kluczami typu keypress &.

Należy pamiętać, że devilspie jest zapisywany jako proces demona i pozostaje aktywny. Demon może zostać zabity po uruchomieniu, a zmiany w oknach będą nadal obowiązywać. Lub można go pozostawić uruchomiony, a za każdym razem, gdy okno zostanie aktywowane, zostanie zastosowana konfiguracja devilspie.

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

Błąd ... to dla Linuksa. Nie wiem, czy istnieje odpowiednik programu dla komputerów Mac. –

+0

Dzięki, działa idealnie dla mnie (Archlinux z LXDE). –

+0

Okno z overrideredircct celowo uniemożliwia podejmowanie ostrości, a tym samym nie można uzyskać kluczowe wydarzenia: http://core.tcl.tk/tk/artifact/7892c68f49012d2d71222ae0e312a1e7dc69a801?txt=1&ln=51-64 To jest linia i komentarz starożytny i można go usunąć. Nie jestem tego pewien. – Hugge

Powiązane problemy