2017-08-02 20 views
0

Ich poste dies, weil ich selbst mit dem Finden einer klaren Antwort auf dieses Problem gekämpft habe. . .Deaktivieren Beenden (oder [X]) in tkinter Fenster

Auf der Suche nach dem Versuch, einen Fortschrittsbalken für mein Programm zu erstellen, finde ich, dass es schwierig ist, mit Tkinter zu tun. Um das Erstellen eines Fortschrittsbalkens zu erreichen, ohne in die gefürchtete "Hauptschleife" zu laufen, I opted to make a class out of the progress bar using threads. Durch viele Versuche habe ich festgestellt, dass es aufgrund der Verwendung von Multithreading nicht viel gibt, das angepasst werden kann (tkinter mag im Hauptthread sein). Hier sind zwei Optionen, die ich versucht habe, um ein Drittel gefolgt, die am besten meine Bedürfnisse paßt:

Option 1: Mit einer Callback-Funktion

mit dem folgenden Code:

import tkinter as tk 
import tkinter.ttk as ttk 
import threading 


class ProgressbarApp(threading.Thread): 

    def __init__(self, max_value: int): 
     self.max_value = max_value 

     self.root = None 
     self.pb = None 

     threading.Thread.__init__(self) 
     self.lock = threading.Lock() # (1) 
     self.lock.acquire()    # (2) 
     self.start() 

     # (1) Makes sure progressbar is fully loaded before executing anything 
     with self.lock: 
      return 

    def close(self): 
     self.root.quit() 

    def run(self): 

     self.root = tk.Tk() 
     self.root.protocol("WM_DELETE_WINDOW", self.__callback) 

     self.pb = ttk.Progressbar(self.root, orient='horizontal', length=400, mode='determinate') 
     self.pb['value'] = 0 
     self.pb['maximum'] = self.max_value 
     self.pb.pack() 

     self.lock.release()    # (2) Will release lock when finished 
     self.root.mainloop() 

    def update(self, value: int): 
     self.pb['value'] = value 

    @staticmethod 
    def __callback(): 
     return 

if __name__ == '__main__': 
    interval = 100000 
    my_pb = ProgressbarApp(interval) 

    for i in range(interval): 
     my_pb.update(i) 

    my_pb.close() 

    # Other stuff goes on . . . 

Wo

self.root.protocol("WM_DELETE_WINDOW", self.__callback) 

Verhindert, dass das Fenster geschlossen wird. Wenn Sie jedoch die Schaltfläche Beenden oder [X] gedrückt halten, wird die Fortschrittsanzeige eingefroren, bis der Benutzer die Schaltfläche loslässt. (Die __callback-Funktion wird ständig aufgerufen, um zu verhindern, dass andere Aufgaben abgeschlossen werden).

Option 2: Verwenden root.overriderdirect (True)

den folgenden Code Gegeben:

import tkinter as tk 
import tkinter.ttk as ttk 
import threading 


class ProgressbarApp(threading.Thread): 

    def __init__(self, max_value: int): 
     self.max_value = max_value 

     self.root = None 
     self.pb = None 

     threading.Thread.__init__(self) 
     self.lock = threading.Lock() # (1) 
     self.lock.acquire()    # (2) 
     self.start() 

     # (1) Makes sure progressbar is fully loaded before executing anything 
     with self.lock: 
      return 

    def close(self): 
     self.root.quit() 

    def run(self): 

     self.root = tk.Tk() 
     self.root.overrideredirect(True) 

     self.pb = ttk.Progressbar(self.root, orient='horizontal', length=400, mode='determinate') 
     self.pb['value'] = 0 
     self.pb['maximum'] = self.max_value 
     self.pb.pack() 

     self.lock.release()    # (2) Will release lock when finished 
     self.root.mainloop() 

    def update(self, value: int): 
     self.pb['value'] = value 

if __name__ == '__main__': 
    interval = 100000 
    my_pb = ProgressbarApp(interval) 

    for i in range(interval): 
     my_pb.update(i) 

    my_pb.close() 

    # Other stuff goes on . . . 

Wo

self.root.overrideredirect(True) 

alle tkinters Fenster Optionen Löscht. Der Fortschrittsbalken befindet sich jedoch nicht nur an einem ungewöhnlichen Ort, sondern verdeckt auch das Benutzerfenster. Die Fortschrittsanzeige sollte benutzerfreundlich sein.

Option 3: root.attributes Verwendung ('- disabled', True)

der folgende Code Gegeben:

import tkinter as tk 
import tkinter.ttk as ttk 
import threading 


class ProgressbarApp(threading.Thread): 

    def __init__(self, max_value: int): 
     self.max_value = max_value 

     self.root = None 
     self.pb = None 

     threading.Thread.__init__(self) 
     self.lock = threading.Lock() # (1) 
     self.lock.acquire()    # (2) 
     self.start() 

     # (1) Makes sure progressbar is fully loaded before executing anything 
     with self.lock: 
      return 

    def close(self): 
     self.root.quit() 

    def run(self): 

     self.root = tk.Tk() 
     self.root.attributes('-disabled', True) 

     self.pb = ttk.Progressbar(self.root, orient='horizontal', length=400, mode='determinate') 
     self.pb['value'] = 0 
     self.pb['maximum'] = self.max_value 
     self.pb.pack() 

     self.lock.release()    # (2) Will release lock when finished 
     self.root.mainloop() 

    def update(self, value: int): 
     self.pb['value'] = value 

if __name__ == '__main__': 
    interval = 100000 
    my_pb = ProgressbarApp(interval) 

    for i in range(interval): 
     my_pb.update(i) 

    my_pb.close() 

    # Other stuff goes on . . . 

Wo

self.root.attributes('-disabled', True) 

Verhindert, dass Benutzer die Interaktion mit dem Fenster . Dies hat meine Bedürfnisse für dieses Programm am besten erfüllt, da es verhindert, dass das Fenster schließt und immer noch ein schönes Aussehen hat. (Mein einziges kleines Problem ist, dass der Benutzer den Fortschrittsbalken nicht länger minimieren oder verschieben kann).

Wenn es bessere Lösungen gibt, würde ich sie gerne sehen. Hoffentlich hat das jemandem geholfen.

+0

Das ist nicht wie eine Frage aussieht. Ich verstehe nicht, was du verlangst. –

Antwort

0

Sie können eine Funktion erstellen, die nur pass verwendet, um nichts zu tun.

Werfen Sie einen Blick auf die folgende Liste:

import tkinter as tk 


root=tk.Tk() 

def close_program(): 
    root.destroy() 

def disable_event(): 
    pass 

btn = tk.Button(root, text = "Click me to close", command = close_program) 
btn.pack() 

root.protocol("WM_DELETE_WINDOW", disable_event) 

root.mainloop() 

Sie können auch die Symbolleiste entfernen Sie alle zusammen mit root.overrideredirect(True), die den Benutzer bei der Verwendung einer der Symbolleiste verhindern. Das Verlassen root.protocol("WM_DELETE_WINDOW", disable_event) verhindert auch die Verwendung von ALT + F4.

import tkinter as tk 


root=tk.Tk() 
root.geometry("400x400") 
root.overrideredirect(True) 

def close_program(): 
    root.destroy() 

def disable_event(): 
    pass 

btn = tk.Button(root, text = "Click me to close", command = close_program) 
btn.pack() 

root.protocol("WM_DELETE_WINDOW", disable_event) 

root.mainloop() 
+0

Ich mag nicht mit root.overrideredirect (True), weil dann das Fenster in der oberen linken Ecke meines Bildschirms bei der Ausführung fest. Ich werde jedoch versuchen, innerhalb von root.protocol ("WM_DELETE_WINDOW", disable_event) "übergeben". Ich benutzte früher die Rückkehr. –

+0

@JoshuaVanDeren: Sie können 'overrideredirect (True)' verwenden und dann können Sie Ihre eigene benutzerdefinierte Symbolleiste erstellen. Es ist ein bisschen Arbeit, aber ist sehr anpassbar. –

+0

EDIT: mit pass immer noch nicht das Problem behoben. Der Fortschrittsbalken ist immer noch eingefroren, wenn der Benutzer [X] hält @Sierra Mountain Tech Sind Sie sicher, dass Sie während der Fortschrittsbalken in einem Thread ist? Ich bin mehrmals auf ein Problem gestoßen, wo es im Hauptthread sein muss. –

0

anderer Weg, dies zu Fenster zu erreichen:

#!python3 

import tkinter as tk 
from tkinter import ttk 
import threading, time 

import tkinter as tk 
from ctypes import windll, wintypes 

GWL_STYLE = -16 
WS_CHILD = 0x40000000 
WS_SYSMENU = 0x00080000 

SWP_FRAMECHANGED = 0x0020 
SWP_NOACTIVATE = 0x0010 
SWP_NOMOVE = 0x0002 
SWP_NOSIZE = 0x0001 

# write short names for functions and specify argument and return types 
GetWindowLong = windll.user32.GetWindowLongW 
GetWindowLong.restype = wintypes.ULONG 
GetWindowLong.argtpes = (wintypes.HWND, wintypes.INT) 

SetWindowLong = windll.user32.SetWindowLongW 
SetWindowLong.restype = wintypes.ULONG 
SetWindowLong.argtpes = (wintypes.HWND, wintypes.INT, wintypes.ULONG) 

SetWindowPos = windll.user32.SetWindowPos 

class App(tk.Tk): 
    def __init__(self): 
     tk.Tk.__init__(self) 
     self.pb = ttk.Progressbar(self, orient="horizontal", length=400, mode="determinate", maximum=100) 
     self.pb.pack() 
     tk.Button(self, text="Remove buttons", command=self.remove_buttons).pack() 
     tk.Button(self, text="Add buttons", command=self.add_buttons).pack() 


    def start(self): 
     self.t = threading.Thread(target=self.loop) 
     self.t.start() 

    def loop(self): 
     while True: 
      for num in range(0, 100): 
       self.pb['value']=num 
       time.sleep(0.1) 

    def _get_hwnd(self): 
     w_id = self.winfo_id() # gets handle 
     style = GetWindowLong(w_id, GWL_STYLE) # get existing style 
     newstyle = style & ~WS_CHILD # remove child style 
     res = SetWindowLong(w_id, GWL_STYLE, newstyle) # set new style 
     res = SetWindowPos(w_id, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE) 
     hwnd = int(self.wm_frame(), 16) # find handle of parent 
     res = SetWindowLong(w_id, GWL_STYLE, style) # set back to old style 
     res = SetWindowPos(w_id, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE) 
     return hwnd # return parents handle 

    def remove_buttons(self): 
     hwnd = self._get_hwnd() 
     style = GetWindowLong(hwnd, GWL_STYLE) # get existing style 
     style = style & ~WS_SYSMENU 
     res = SetWindowLong(hwnd, GWL_STYLE, style) 
     res = SetWindowPos(hwnd, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE) 

    def add_buttons(self): 
     hwnd = self._get_hwnd() 
     style = GetWindowLong(hwnd, GWL_STYLE) # get existing style 
     style = style | WS_SYSMENU 
     res = SetWindowLong(hwnd, GWL_STYLE, style) 
     res = SetWindowPos(hwnd, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE) 

if __name__ == "__main__": 
    app = App() 
    app.start() 
    app.mainloop() 
+0

Sieht kompliziert aus, scheint aber durchführbar! –

+0

es sieht kompliziert aus, weil tkinter nicht gut mit der Windows API funktioniert, um ein Handle zum Elternfenster zu erhalten, der Hacker, um dies zu umgehen, ist in der Funktion _get_hwnd, und die Funktionen hinzufügen oder entfernen knöpfen den Stil der Fenster, so wie es ist, dann werden durch Hinzufügen von die Stile entfernt, die benötigt werden, um alle Schaltflächen in der Titelleiste zu entfernen, da es den vorhandenen Stil ändert, der mit den meisten Fensterstilen funktionieren sollte –