2016-01-03 24 views
12

Tôi đang viết một chương trình bằng Python với giao diện người dùng Tkinter. Tôi muốn có một cửa sổ nhỏ không có thanh tiêu đề. Cửa sổ này phải nhận đầu vào bàn phím. Tôi không cầu kỳ cho dù điều này là trong các hình thức của một phụ tùng nhập cảnh hoặc chỉ cần ràng buộc để KeyPress. overrideredirect(True) thường là cách tắt thanh tiêu đề. Thật không may, (ngoại trừ trong Windows), điều này dường như để ngăn chặn nhiều sự kiện nhận được. Tôi đã viết mã này để minh họa sự cố:Tách tắt của Tkinter ngăn chặn các sự kiện nhất định trong Mac và 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() 

Điều này tạo ra một cửa sổ nhỏ (không có thanh tiêu đề) in tên sự kiện phổ biến khi nhận được sự kiện. Tôi đã chạy tập lệnh này trên Windows 7, Mac OSX (El Capitan) và Ubuntu 14.04.1. Tôi chỉ chạy Ubuntu trong một máy ảo (VMWare).

  • Trong Windows, điều này dường như hoạt động như mong đợi. Tất cả các sự kiện mà kiểm tra mã của tôi có thể được nhận.

  • Trong Ubuntu, cửa sổ Tkinter nhận <Enter>, <Leave>, và <Button-1> sự kiện như mong đợi, nhưng <KeyPress>, <FocusIn>, và <FocusOut> không bao giờ nhận được. Trong thực tế, ngay cả sau khi cửa sổ đã được nhấp vào, cửa sổ cuối cùng với tiêu điểm tiếp tục nhận được các phím bấm.

  • Trong OSX, cửa sổ Tkinter nhận <Button-1> sự kiện như mong đợi, nhưng <KeyPress>, <FocusIn>, và <FocusOut> không bao giờ nhận được. Cửa sổ cuối cùng với tiêu điểm không tiếp tục nhận các lần nhấn phím như trong Ubuntu. Các sự kiện <Enter><Leave> hoạt động một cách kỳ lạ. Sự kiện <Enter> không được nhận cho đến khi cửa sổ được nhấp. Sau đó, khi sự kiện <Leave> xảy ra, cửa sổ cần được nhấp lại để nhận sự kiện <Enter> khác.

Tôi cũng đã thử self.focus_force() ngay trước khi kết thúc chức năng __init__. Điều này làm cho cửa sổ nhận được sự kiện <FocusIn> khi chương trình bắt đầu nhưng không nhận được thêm <KeyPress>, <FocusIn> hoặc <FocusOut> sự kiện không bao giờ được nhận.

Cuối cùng, câu hỏi của tôi là: có cách nào để ẩn thanh tiêu đề nhưng tiếp tục nhận đầu vào bàn phím trong OSX và Linux không?


Tôi biết một số câu hỏi khác liên quan đến cùng vấn đề này. Trong ba câu hỏi sau:

Câu trả lời được chấp nhận là sử dụng self.attributes('-fullscreen', True), mà sẽ không làm việc cho tôi khi tôi muốn có một cửa sổ nhỏ bé, không phải là một ứng dụng toàn màn hình.

Có một câu hỏi khác: Tkinter overrideredirect no longer receiving event bindings. Điều này dường như rất gần với câu hỏi của tôi, nhưng cung cấp ít chi tiết hơn và không có câu trả lời.


Cập nhật: Tôi đã cố gắng để điều tra về cơ chế cơ bản của vấn đề của tôi. Tôi biết rằng Tkinter là một wrapper xung quanh Tcl/Tk, vì vậy tôi nghĩ rằng tôi sẽ cố gắng viết lại mã của tôi trong Tcl. Tôi không thực sự biết Tcl, nhưng tôi nghĩ rằng tôi đã quản lý (nhiều hay ít) dịch Python của mình:

#!/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>"} 

Tôi đã thử chương trình kết quả trong Windows và Mac OSX. Trong Windows, tôi nhận được <KeyPress> sự kiện, nhưng trong OSX thì không. Nếu không có đường dây wm overrideredirect . True, OSX sẽ không nhận được sự kiện <KeyPress>. Vì vậy, có vẻ như vấn đề này không phải là với Python, nhưng với Tcl/Tk.

+0

Tắt 'mainloop' và' cách truy cập lại lần nữa? 'Binding (key listen) hoạt động với các phần tử phụ. Bạn đã đạt được một ứng dụng 'ghost' nếu' listener' là 'frozen'. – dsgdfg

+0

Một thủ thuật: Làm cho nút [X] của bạn thành vòng lặp chính (do đó, 'deiconify()')! – dsgdfg

Trả lời

2

Tôi đã gửi báo cáo lỗi cho Tk cho tình huống này.

Bạn có thể sử dụng chương trình devilspie để xóa đồ trang trí khỏi cửa sổ của mình. Sử dụng lệnh wm title . myname để đặt tên cho cửa sổ của bạn và sử dụng tên đó trong đoạn cấu hình devilspie bên dưới. Xóa lệnh overrideredirect khỏi chương trình của bạn.

Tôi đã thử nghiệm điều này (dưới dạng chương trình Tk) và cửa sổ chưa được trang bị sẽ vẫn nhận được các phím bấm & vv.

Lưu ý rằng devilspie được viết dưới dạng quy trình daemon và vẫn hoạt động. Daemon có thể bị giết sau khi nó được khởi động và thay đổi cửa sổ được thực hiện sẽ vẫn có hiệu lực. Hoặc nó có thể được để lại, và bất cứ khi nào cửa sổ của bạn được kích hoạt, cấu hình devilspie sẽ được áp dụng.

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

Err ... đó là dành cho Linux. Tôi không biết nếu có một chương trình tương đương cho Mac. –

+0

Cảm ơn, hoạt động hoàn hảo cho tôi (Archlinux với LXDE). –

+0

Một cửa sổ với overrideredircct được cố ý ngăn không tập trung, và do đó không thể nhận được các sự kiện quan trọng: http://core.tcl.tk/tk/artifact/7892c68f49012d2d71222ae0e312a1e7dc69a801?txt=1&ln=51-64 Dòng và nhận xét đó là cổ đại, và có thể có thể loại bỏ. Không chắc chắn. – Hugge

Các vấn đề liên quan