From rei4dan at gmail.com Thu Feb 4 07:58:11 2016 From: rei4dan at gmail.com (Reinis Danne) Date: Thu, 4 Feb 2016 14:58:11 +0200 Subject: [Tkinter-discuss] Moving object on page leaves afterimage on Python 3 but not on 2 Message-ID: <20160204125811.GA24608@RD-OC.homerouter.cpe> Hi! I'm porting a chemistry drawing program BKChem [1] to Python 3 using the shared codebase between versions 2 and 3. It is working fine on Python 2, but if I run it with Python 3 I have an issue with redrawing the page. The issue is that if I move an object by mouse then it leaves an image of the object in the old place while moving the object to the new place (Python 3). There is no such afterimage with Python 2. Similar effect can be achieved if object is deleted or its creation is undoed - object is removed, but lines remain. It seems that text is updated correctly, only bonds (lines) have the issue. Any ideas where the bug might be and how to debug it? As far as I understand the moving of the object and updating the drawing is up to tkinter or the underlying tk. It could be an issue with the app losing track of the object, but in that case it wouldn't create new lines (if I drag and drop an object several times, several afterimages remain, but not 100% of the time; usually the very first move works, but later ones produce the afterimages). So I think it is a lower level issue. To test: $ git clone https://gitlab.com/bkchem/bkchem.git $ cd bkchem $ git submodule init $ git submodule update $ python3 bkchem/bkchem.py Regards, Reinis [1] https://gitlab.com/bkchem/bkchem From klappnase at web.de Fri Feb 5 04:29:42 2016 From: klappnase at web.de (Michael Lange) Date: Fri, 5 Feb 2016 10:29:42 +0100 Subject: [Tkinter-discuss] Moving object on page leaves afterimage on Python 3 but not on 2 In-Reply-To: <20160204125811.GA24608@RD-OC.homerouter.cpe> References: <20160204125811.GA24608@RD-OC.homerouter.cpe> Message-ID: <20160205102942.3a76870e21609556d102276b@web.de> Hi, On Thu, 4 Feb 2016 14:58:11 +0200 Reinis Danne wrote: > To test: > $ git clone https://gitlab.com/bkchem/bkchem.git > $ cd bkchem > $ git submodule init > $ git submodule update > $ python3 bkchem/bkchem.py this looks like quite a complex piece of code. Is it possible for you to provide a simpler example that exhibits the problem? Some additional information might also be helpful to track down the problem: * which platform are you running (Windows / X / Mac)? * do your Python 2 / 3 versions share the same Tk version? * which Tk version(s) do you use? Without having reviewed the code I can only try a quick shot in the dark: maybe Python 2 and 3 do *not* use the same Tk and these Tk versions behave slightly differently. Maybe inserting additional calls to update_idletasks() here and there might help. At a quick glance the redraw() method in your graphics module might be a candidate for this. Have you already tried that? Best regards Michael .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. There's nothing disgusting about it [the Companion]. It's just another life form, that's all. You get used to those things. -- McCoy, "Metamorphosis", stardate 3219.8 From rei4dan at gmail.com Fri Feb 5 09:56:58 2016 From: rei4dan at gmail.com (Reinis Danne) Date: Fri, 5 Feb 2016 16:56:58 +0200 Subject: [Tkinter-discuss] Moving object on page leaves afterimage on Python 3 but not on 2 In-Reply-To: <20160205102942.3a76870e21609556d102276b@web.de> References: <20160204125811.GA24608@RD-OC.homerouter.cpe> <20160205102942.3a76870e21609556d102276b@web.de> Message-ID: <20160205145658.GC24608@RD-OC.homerouter.cpe> On Fri, Feb 05, 2016 at 10:29:42AM +0100, Michael Lange wrote: > Hi, > > On Thu, 4 Feb 2016 14:58:11 +0200 > Reinis Danne wrote: > > > To test: > > $ git clone https://gitlab.com/bkchem/bkchem.git > > $ cd bkchem > > $ git submodule init > > $ git submodule update > > $ python3 bkchem/bkchem.py > > this looks like quite a complex piece of code. Is it possible for you to > provide a simpler example that exhibits the problem? Ok. With the help of traceback module and some print statements I have isolated where the issue is and provide a small example code in attachment. > Some additional information might also be helpful to track down the > problem: > * which platform are you running (Windows / X / Mac)? I'm running on Gentoo ~amd64 but have tested also on Linux Mint. > * do your Python 2 / 3 versions share the same Tk version? > * which Tk version(s) do you use? > Yes, I have only tk-5.6.4 installed. > Without having reviewed the code I can only try a quick shot in the dark: > maybe Python 2 and 3 do *not* use the same Tk and these Tk versions > behave slightly differently. Maybe inserting additional calls to > update_idletasks() here and there might help. At a quick glance the > redraw() method in your graphics module might be a candidate for this. > Have you already tried that? > > Best regards > > Michael > Thanks for the help! The main drawing routines for BKChem are located in paper.py, but the issue seems to be with bond.py::bond.delete(). It uses map() to call Canvas.delete() for every item in a list. It works in Python 2, but in Python 3 it doesn't delete the item(s). Replacing this: map(self.paper.delete, items) with this: for i in items: self.paper.delete(i) fixes the problem. I don't understand why the difference. That code is equivalent in both cases as far as I know. Is there a bug or I'm using map() incorrectly? Reinis -------------- next part -------------- A non-text attachment was scrubbed... Name: test.py Type: text/x-python Size: 599 bytes Desc: not available URL: From klappnase at web.de Fri Feb 5 11:03:50 2016 From: klappnase at web.de (Michael Lange) Date: Fri, 5 Feb 2016 17:03:50 +0100 Subject: [Tkinter-discuss] Moving object on page leaves afterimage on Python 3 but not on 2 In-Reply-To: <20160205145658.GC24608@RD-OC.homerouter.cpe> References: <20160204125811.GA24608@RD-OC.homerouter.cpe> <20160205102942.3a76870e21609556d102276b@web.de> <20160205145658.GC24608@RD-OC.homerouter.cpe> Message-ID: <20160205170350.218bfb4f87327de6059d174c@web.de> Hi, I'm glad you finally got it working! On Fri, 5 Feb 2016 16:56:58 +0200 Reinis Danne wrote: (...) > Replacing this: > map(self.paper.delete, items) > > with this: > for i in items: > self.paper.delete(i) > > fixes the problem. > > I don't understand why the difference. That code is equivalent in > both cases as far as I know. Is there a bug or I'm using map() > incorrectly? I'm not sure about this, the behavior of map() seems to have changed in Python3, I just tried the following with Python2 first: $ python Python 2.7.9 (default, Mar 1 2015, 12:57:24) [GCC 4.9.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> l = ['a', 'b', 'c'] >>> def test(x): ... print(x) ... >>> map(test, l) a b c [None, None, None] >>> and then with Python3: $ python3 Python 3.4.2 (default, Oct 8 2014, 10:45:20) [GCC 4.9.1] on linux Type "help", "copyright", "credits" or "license" for more information. >>> l = ['a', 'b', 'c'] >>> def test(x): ... print(x) ... >>> map(test, l) >>> Personally I hardly ever use map(), so I never noticed that. In fact this change is actually documented, see https://docs.python.org/3.0/whatsnew/3.0.html#views-and-iterators-instead-of-lists : "map() and filter() return iterators. If you really need a list, a quick fix is e.g. list(map(...)), but a better fix is often to use a list comprehension (especially when the original code uses lambda), or rewriting the code so it doesn?t need a list at all. Particularly tricky is map() invoked for the side effects of the function; the correct transformation is to use a regular for loop " Not sure what's the use of these map objects, in Python3 now you can call the map object's __next__() method to iterate. But for your purpose certainly the for loop is the way to go. Best regards Michael .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. Intuition, however illogical, is recognized as a command prerogative. -- Kirk, "Obsession", stardate 3620.7 From bha100710 at gmail.com Fri Feb 5 11:33:51 2016 From: bha100710 at gmail.com (Bhaskar Chaudhary) Date: Fri, 5 Feb 2016 22:03:51 +0530 Subject: [Tkinter-discuss] Moving object on page leaves afterimage on Python 3 but not on 2 In-Reply-To: <20160205170350.218bfb4f87327de6059d174c@web.de> References: <20160204125811.GA24608@RD-OC.homerouter.cpe> <20160205102942.3a76870e21609556d102276b@web.de> <20160205145658.GC24608@RD-OC.homerouter.cpe> <20160205170350.218bfb4f87327de6059d174c@web.de> Message-ID: Hi Reinis Danne I am not sure if this is exactly the cause of your problem, but map() returns a list in Python 2 but an iterator in Python 3. You can place a list around map() like: list(map(self.paper.delete, items)) to ensure that the result is still a list and has the same behavior in both Python 2 and 3. Another difference is that in Python 2 map() continues until the items in the longest of the iterator is exhausted, extending the other arguments with None. In Python 3 this is not the case, a similar behavior can be achieved using itertools.zip_longest. So I guess, your problem is related to one of the above two issues. BTW, really nice program there you have built. regards Bhaskar On 2/5/16, Michael Lange wrote: > Hi, > > I'm glad you finally got it working! > > On Fri, 5 Feb 2016 16:56:58 +0200 > Reinis Danne wrote: > > (...) >> Replacing this: >> map(self.paper.delete, items) >> >> with this: >> for i in items: >> self.paper.delete(i) >> >> fixes the problem. >> >> I don't understand why the difference. That code is equivalent in >> both cases as far as I know. Is there a bug or I'm using map() >> incorrectly? > > I'm not sure about this, the behavior of map() seems to have changed in > Python3, I just tried the following with Python2 first: > > $ python > Python 2.7.9 (default, Mar 1 2015, 12:57:24) > [GCC 4.9.2] on linux2 > Type "help", "copyright", "credits" or "license" for more information. >>>> l = ['a', 'b', 'c'] >>>> def test(x): > ... print(x) > ... >>>> map(test, l) > a > b > c > [None, None, None] >>>> > > and then with Python3: > > $ python3 > Python 3.4.2 (default, Oct 8 2014, 10:45:20) > [GCC 4.9.1] on linux > Type "help", "copyright", "credits" or "license" for more information. >>>> l = ['a', 'b', 'c'] >>>> def test(x): > ... print(x) > ... >>>> map(test, l) > >>>> > > Personally I hardly ever use map(), so I never noticed that. In fact this > change is actually documented, see > https://docs.python.org/3.0/whatsnew/3.0.html#views-and-iterators-instead-of-lists > : > > "map() and filter() return iterators. If you really need a list, a quick > fix is e.g. list(map(...)), but a better fix is often to use a list > comprehension (especially when the original code uses lambda), or > rewriting the code so it doesn?t need a list at all. Particularly tricky > is map() invoked for the side effects of the function; the correct > transformation is to use a regular for loop " > > Not sure what's the use of these map objects, in Python3 now you can call > the map object's __next__() method to iterate. > But for your purpose certainly the for loop is the way to go. > > Best regards > > Michael > > > .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. > > Intuition, however illogical, is recognized as a command prerogative. > -- Kirk, "Obsession", stardate 3620.7 > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss at python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss > From rei4dan at gmail.com Fri Feb 5 12:22:18 2016 From: rei4dan at gmail.com (Reinis Danne) Date: Fri, 5 Feb 2016 19:22:18 +0200 Subject: [Tkinter-discuss] Moving object on page leaves afterimage on Python 3 but not on 2 In-Reply-To: <20160205170350.218bfb4f87327de6059d174c@web.de> References: <20160204125811.GA24608@RD-OC.homerouter.cpe> <20160205102942.3a76870e21609556d102276b@web.de> <20160205145658.GC24608@RD-OC.homerouter.cpe> <20160205170350.218bfb4f87327de6059d174c@web.de> Message-ID: <20160205172218.GD24608@RD-OC.homerouter.cpe> On Fri, Feb 05, 2016 at 05:03:50PM +0100, Michael Lange wrote: > > Personally I hardly ever use map(), so I never noticed that. In fact this > change is actually documented, see > https://docs.python.org/3.0/whatsnew/3.0.html#views-and-iterators-instead-of-lists : > > "map() and filter() return iterators. If you really need a list, a quick > fix is e.g. list(map(...)), but a better fix is often to use a list > comprehension (especially when the original code uses lambda), or > rewriting the code so it doesn?t need a list at all. Particularly tricky > is map() invoked for the side effects of the function; the correct > transformation is to use a regular for loop " > > Not sure what's the use of these map objects, in Python3 now you can call > the map object's __next__() method to iterate. > But for your purpose certainly the for loop is the way to go. > > Best regards > > Michael Thanks, that clears it up. Somehow I had missed this. Reinis From rei4dan at gmail.com Fri Feb 5 12:29:43 2016 From: rei4dan at gmail.com (Reinis Danne) Date: Fri, 5 Feb 2016 19:29:43 +0200 Subject: [Tkinter-discuss] Moving object on page leaves afterimage on Python 3 but not on 2 In-Reply-To: References: <20160204125811.GA24608@RD-OC.homerouter.cpe> <20160205102942.3a76870e21609556d102276b@web.de> <20160205145658.GC24608@RD-OC.homerouter.cpe> <20160205170350.218bfb4f87327de6059d174c@web.de> Message-ID: <20160205172943.GE24608@RD-OC.homerouter.cpe> On Fri, Feb 05, 2016 at 10:03:51PM +0530, Bhaskar Chaudhary wrote: > Hi Reinis Danne > > I am not sure if this is exactly the cause of your problem, but > map() returns a list in Python 2 but an iterator in Python 3. > > You can place a list around map() like: > list(map(self.paper.delete, items)) > to ensure that the result is still a list and has the same behavior in > both Python 2 and 3. > > Another difference is that in Python 2 map() continues until the items > in the longest of the iterator is exhausted, extending the other > arguments with None. In Python 3 this is not the case, a similar > behavior can be achieved using itertools.zip_longest. > > So I guess, your problem is related to one of the above two issues. > > BTW, really nice program there you have built. Thanks! But I'm only a lowly maintainer and porting to Python 3. The credit for writing BKChem goes to Beda Kosata. Reinis From rei4dan at gmail.com Fri Feb 5 14:44:04 2016 From: rei4dan at gmail.com (Reinis Danne) Date: Fri, 5 Feb 2016 21:44:04 +0200 Subject: [Tkinter-discuss] Debugging non-zero exit code Message-ID: <20160205194404.GF24608@RD-OC.homerouter.cpe> Hi! I'm trying to debug why BKChem prints '0' and exits with exit code 1. As far as I can tell that is not directly in the app. I tried to debug it with gdb, but it tried (and failed) to start the app again after I have pressed the close button, so I didn't get any useful info out of it. How could I debug this issue? The application setup code is in bkchem.py and the main class is in main.py. To test: $ git clone https://gitlab.com/bkchem/bkchem.git $ cd bkchem $ git submodule init $ git submodule update $ python bkchem/bkchem.py Should work with Python 2 and 3. Any tips how to deal with this would be appreciated. Also it would be great if someone could review the tkinter setup sequence. Reinis From klappnase at web.de Fri Feb 5 15:09:39 2016 From: klappnase at web.de (Michael Lange) Date: Fri, 5 Feb 2016 21:09:39 +0100 Subject: [Tkinter-discuss] Debugging non-zero exit code In-Reply-To: <20160205194404.GF24608@RD-OC.homerouter.cpe> References: <20160205194404.GF24608@RD-OC.homerouter.cpe> Message-ID: <20160205210939.d59b1a0a9e9c7be3a6e59842@web.de> Hi, On Fri, 5 Feb 2016 21:44:04 +0200 Reinis Danne wrote: > Hi! > > I'm trying to debug why BKChem prints '0' and exits with exit > code 1. As far as I can tell that is not directly in the app. > I tried to debug it with gdb, but it tried (and failed) to start > the app again after I have pressed the close button, so I didn't > get any useful info out of it. How could I debug this issue? just a guess from a quick glance in bkchem.py: in the if __name__ == '__main__' part of your code you have something like this: myapp.geometry(geometry) myapp.update_idletasks() myapp.deiconify() myapp.mainloop() myapp.destroy() In the following minimal example: from Tkinter import * root = Tk() root.mainloop() root.destroy() when you click the "X" button in the window corner to close the window, you will get an exit code 1 along with an error message like this: Traceback (most recent call last): File "test.py", line 6, in root.destroy() File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1858, in destroy self.tk.call('destroy', self._w) _tkinter.TclError: can't invoke "destroy" command: application has been destroyed Maybe something like this is happening? If yes, the solution is to add a handler for the responsible window manager protocol, as in: from Tkinter import * root = Tk() root.wm_protocol('WM_DELETE_WINDOW', root.quit) root.mainloop() root.destroy() Best regards Michael .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. I've already got a female to worry about. Her name is the Enterprise. -- Kirk, "The Corbomite Maneuver", stardate 1514.0 From rei4dan at gmail.com Sat Feb 6 11:43:07 2016 From: rei4dan at gmail.com (Reinis Danne) Date: Sat, 6 Feb 2016 18:43:07 +0200 Subject: [Tkinter-discuss] Debugging non-zero exit code In-Reply-To: <20160205210939.d59b1a0a9e9c7be3a6e59842@web.de> References: <20160205194404.GF24608@RD-OC.homerouter.cpe> <20160205210939.d59b1a0a9e9c7be3a6e59842@web.de> Message-ID: <20160206164307.GG24608@RD-OC.homerouter.cpe> On Fri, Feb 05, 2016 at 09:09:39PM +0100, Michael Lange wrote: > Hi, > > On Fri, 5 Feb 2016 21:44:04 +0200 > Reinis Danne wrote: > > > Hi! > > > > I'm trying to debug why BKChem prints '0' and exits with exit > > code 1. As far as I can tell that is not directly in the app. > > I tried to debug it with gdb, but it tried (and failed) to start > > the app again after I have pressed the close button, so I didn't > > get any useful info out of it. How could I debug this issue? > > just a guess from a quick glance in bkchem.py: > in the if __name__ == '__main__' part of your code you have something > like this: > > myapp.geometry(geometry) > myapp.update_idletasks() > myapp.deiconify() > myapp.mainloop() > myapp.destroy() > > In the following minimal example: > > from Tkinter import * > root = Tk() > root.mainloop() > root.destroy() > > when you click the "X" button in the window corner to close the window, > you will get an exit code 1 along with an error message like this: > > Traceback (most recent call last): > File "test.py", line 6, in > root.destroy() > File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1858, in destroy > self.tk.call('destroy', self._w) > _tkinter.TclError: can't invoke "destroy" command: application has been > destroyed > > Maybe something like this is happening? > > If yes, the solution is to add a handler for the responsible window > manager protocol, as in: > > from Tkinter import * > root = Tk() > root.wm_protocol('WM_DELETE_WINDOW', root.quit) > root.mainloop() > root.destroy() > > > Best regards > > Michael Thanks for the help! Adding root.wm_protocol('WM_DELETE_WINDOW', root.quit) worked indeed, but only for the close button. It didn't work if I closed the window with Alt+F4 or File->Exit. On further inspection it already used WM_DELETE_WINDOW protocol in main.py::BKChem.initialize() like this: self.protocol("WM_DELETE_WINDOW", self._quit) And then called self.quit() and sys.exit(): def _quit(self): # Do some clean-up self.quit() if os.name != "nt": sys.exit(0) The issue seems to be with the sys.exit() call, removing that fixes the issue. It was introduced in commit ca8deafe48de3f358c09e9a7b9532fa56caff6b4 and was supposed to deal with exiting when editPool widget has been active. Without it the window is not closed if editPool widget has been active (press the button with simbol "N" and click on the paper to activate editPool widget and Enter to deactivate it). Another issue is the self.quit() call. According to the documentation [1] the WM_DELETE_WINDOW protocol handler should call self.destroy(). If I move the bkchem.py::myapp.destroy() to main.py::BKChem._quit() in place of self.quit() call everything still works. So what is the difference between destroy() and quit() and which is better here? >From documentation [2]: "The mainloop call enters the Tk event loop, in which the application will stay until the quit method is called (just click the QUIT button), or the window is closed. The destroy call is only required if you run this example under certain development environments; it explicitly destroys the main window when the event loop is terminated. Some development environments won?t terminate the Python process unless this is done." So is either way (with or without the quit() call) ok? It turns out that using destroy() in place of quit() allows to close the app properly even in the case when editPool has been active so there is no need for sys.exit() call. The remaining question is if I'm supposed to do some extra clean-up when deactivating editPool (beyond calling edit_pool.py::editPool._cancel() on it if it is still active; that calls editPool.quit() among other things)? How? Calling destroy() at exit time feels like dealing with the issue with brute force instead of properly cleaning up when deactivating the widget. Reinis [1] http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm#protocols [2] http://effbot.org/tkinterbook/tkinter-hello-again.htm From klappnase at web.de Sat Feb 6 14:02:43 2016 From: klappnase at web.de (Michael Lange) Date: Sat, 6 Feb 2016 20:02:43 +0100 Subject: [Tkinter-discuss] Debugging non-zero exit code In-Reply-To: <20160206164307.GG24608@RD-OC.homerouter.cpe> References: <20160205194404.GF24608@RD-OC.homerouter.cpe> <20160205210939.d59b1a0a9e9c7be3a6e59842@web.de> <20160206164307.GG24608@RD-OC.homerouter.cpe> Message-ID: <20160206200243.1d34a2b64aa35712ff5366c6@web.de> Hi, On Sat, 6 Feb 2016 18:43:07 +0200 Reinis Danne wrote: (...) > Adding root.wm_protocol('WM_DELETE_WINDOW', root.quit) worked > indeed, but only for the close button. It didn't work if I closed > the window with Alt+F4 or File->Exit. On further inspection it > already used WM_DELETE_WINDOW protocol in > main.py::BKChem.initialize() like this: That's odd, maybe the binding for Alt+F4 was overridden somewhere else in the code? And doesn't File->Exit call the same _quit() procedure as the one one linked to WM_DELETE_WINDOW? (...) > Calling destroy() at exit time feels like dealing with the issue > with brute force instead of properly cleaning up when > deactivating the widget. destroy() itself does not end the application, only the Tk part. This simple example may help to illustrate the difference between quit() and destroy(): from Tkinter import * def clean_up(): print('Cleaning up.') return 0 root = Tk() root.wm_protocol('WM_DELETE_WINDOW', root.quit) l = Label(root, text='foo') l.pack(padx=100, pady=100) root.mainloop() print(l['text']) root.destroy() exitcode = clean_up() print(l['text']) sys.exit(exitcode) As you can see, after quit() is called, the Label widget can still be accessed, which fails (and causes the app to crash) after destroy() was called. You can also very well perform some clean up routine after destroy() has been called, as long as it does not require Tk to still be "alive", As far as sys.exit() is concerned, I think it should not be necessary to use it unless you want to control your app's exit code. Using sys.exit() to end the app rather seems to be "brute force" than destroy(). I have no idea though why the window won't close without it. Does your app use threads? Improper use of threads at least might explain all sorts of odd behavior... Best regards Michael .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. "There's only one kind of woman ..." "Or man, for that matter. You either believe in yourself or you don't." -- Kirk and Harry Mudd, "Mudd's Women", stardate 1330.1 From rei4dan at gmail.com Sat Feb 6 15:22:38 2016 From: rei4dan at gmail.com (Reinis Danne) Date: Sat, 6 Feb 2016 22:22:38 +0200 Subject: [Tkinter-discuss] Debugging non-zero exit code In-Reply-To: <20160206200243.1d34a2b64aa35712ff5366c6@web.de> References: <20160205194404.GF24608@RD-OC.homerouter.cpe> <20160205210939.d59b1a0a9e9c7be3a6e59842@web.de> <20160206164307.GG24608@RD-OC.homerouter.cpe> <20160206200243.1d34a2b64aa35712ff5366c6@web.de> Message-ID: <20160206202238.GH24608@RD-OC.homerouter.cpe> On Sat, Feb 06, 2016 at 08:02:43PM +0100, Michael Lange wrote: > Hi, > > On Sat, 6 Feb 2016 18:43:07 +0200 > Reinis Danne wrote: > > (...) > > Adding root.wm_protocol('WM_DELETE_WINDOW', root.quit) worked > > indeed, but only for the close button. It didn't work if I closed > > the window with Alt+F4 or File->Exit. On further inspection it > > already used WM_DELETE_WINDOW protocol in > > main.py::BKChem.initialize() like this: > > That's odd, maybe the binding for Alt+F4 was overridden somewhere else > in the code? And doesn't File->Exit call the same _quit() procedure as > the one one linked to WM_DELETE_WINDOW? > > (...) > > Calling destroy() at exit time feels like dealing with the issue > > with brute force instead of properly cleaning up when > > deactivating the widget. > > destroy() itself does not end the application, only the Tk part. > This simple example may help to illustrate the difference between quit() > and destroy(): > > from Tkinter import * > > def clean_up(): > print('Cleaning up.') > return 0 > root = Tk() > root.wm_protocol('WM_DELETE_WINDOW', root.quit) > l = Label(root, text='foo') > l.pack(padx=100, pady=100) > root.mainloop() > print(l['text']) > root.destroy() > exitcode = clean_up() > print(l['text']) > sys.exit(exitcode) > > > As you can see, after quit() is called, the Label widget can still be > accessed, which fails (and causes the app to crash) after destroy() was > called. You can also very well perform some clean up routine after > destroy() has been called, as long as it does not require Tk to still be > "alive", > As far as sys.exit() is concerned, I think it should not be necessary to > use it unless you want to control your app's exit code. Using sys.exit() > to end the app rather seems to be "brute force" than destroy(). I have no > idea though why the window won't close without it. Does your app use > threads? Improper use of threads at least might explain all sorts of odd > behavior... No, it is not using threads. The issue seems to be caused by the presence of another mainloop() (see edit_pool.py::editPool.activate()). I now noticed that editPool calls editPool.mainloop() during activation and then editPool.quit() during deactivation, but the widget itself remains. When calling app.quit() the application main loop might be terminated, but it doesn't get to execute the app.destroy() call, probably because the editPool widget is still alive and needs to be destroyed before continuing. The source of non-zero exit code with the sys.exit() call seems to be the presence of the app and editPool widgets left after the app.quit(). It seems that calling app.destroy() in place of app.quit() takes care of destroying also the editPool widget and the app terminates cleanly. I don't want to destroy editPool when deactivated, because I want the widget to be visible in the UI (instead of blank space). So the solution of calling app.destroy() in place of app.quit() seems to be the correct one for this case. Is such use of nested mainloops considered an error? Is there a better way of doing this? Reinis From klappnase at web.de Sat Feb 6 17:10:04 2016 From: klappnase at web.de (Michael Lange) Date: Sat, 6 Feb 2016 23:10:04 +0100 Subject: [Tkinter-discuss] Debugging non-zero exit code In-Reply-To: <20160206202238.GH24608@RD-OC.homerouter.cpe> References: <20160205194404.GF24608@RD-OC.homerouter.cpe> <20160205210939.d59b1a0a9e9c7be3a6e59842@web.de> <20160206164307.GG24608@RD-OC.homerouter.cpe> <20160206200243.1d34a2b64aa35712ff5366c6@web.de> <20160206202238.GH24608@RD-OC.homerouter.cpe> Message-ID: <20160206231004.4e35e52dce0e1f63fad2e554@web.de> Hi, On Sat, 6 Feb 2016 22:22:38 +0200 Reinis Danne wrote: > No, it is not using threads. The issue seems to be caused by the > presence of another mainloop() (see > edit_pool.py::editPool.activate()). I now noticed that editPool > calls editPool.mainloop() during activation and then > editPool.quit() during deactivation, but the widget itself > remains. ahaa, multiple mainloops are one thing that should *never* be done! Not sure what you mean with "deactivation", is that something like a "readonly" or "disabled" state? If so, why not just change the widget state to "readonly" or "disabled"? > I don't want to destroy editPool when deactivated, because I want > the widget to be visible in the UI (instead of blank space). So > the solution of calling app.destroy() in place of app.quit() > seems to be the correct one for this case. Nope, I disagree! If you want your widget to remain visible but disabled, then you should change its state to "disabled" but please leave the mainloop alone. > Is such use of nested mainloops considered an error? Is there a > better way of doing this? Yes and yes. I am still not sure what exactly you want to achieve with two mainloops, but you certainly don't need them. If you need more than one toplevel window, use Toplevel() instead of a second Tk(). If you want to temporarily hide a Tk or Toplevel window without destroying it altogether, use withdraw(). If you want to temporarily hide a non-toplevel widget, use pack/grid_forget(). If you want a widget to remain visible but inactive, set its state to "disabled" or "readonly", which ever fits best. If it is a custom "mega-widget" (as the editpool appears to be), simply set all of its subwidgets to state="disabled". Best regards Michael .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. A princess should not be afraid -- not with a brave knight to protect her. -- McCoy, "Shore Leave", stardate 3025.3 From rei4dan at gmail.com Sun Feb 7 02:35:53 2016 From: rei4dan at gmail.com (Reinis Danne) Date: Sun, 7 Feb 2016 09:35:53 +0200 Subject: [Tkinter-discuss] Debugging non-zero exit code In-Reply-To: <20160206231004.4e35e52dce0e1f63fad2e554@web.de> References: <20160205194404.GF24608@RD-OC.homerouter.cpe> <20160205210939.d59b1a0a9e9c7be3a6e59842@web.de> <20160206164307.GG24608@RD-OC.homerouter.cpe> <20160206200243.1d34a2b64aa35712ff5366c6@web.de> <20160206202238.GH24608@RD-OC.homerouter.cpe> <20160206231004.4e35e52dce0e1f63fad2e554@web.de> Message-ID: <20160207073553.GI24608@RD-OC.homerouter.cpe> On Sat, Feb 06, 2016 at 11:10:04PM +0100, Michael Lange wrote: > Hi, > > On Sat, 6 Feb 2016 22:22:38 +0200 > Reinis Danne wrote: > > > No, it is not using threads. The issue seems to be caused by the > > presence of another mainloop() (see > > edit_pool.py::editPool.activate()). I now noticed that editPool > > calls editPool.mainloop() during activation and then > > editPool.quit() during deactivation, but the widget itself > > remains. > > ahaa, multiple mainloops are one thing that should *never* be done! > > Not sure what you mean with "deactivation", is that something like a > "readonly" or "disabled" state? If so, why not just change the widget > state to "readonly" or "disabled"? I mean "disabled" state, the widget alredy is set to disabled. > > I don't want to destroy editPool when deactivated, because I want > > the widget to be visible in the UI (instead of blank space). So > > the solution of calling app.destroy() in place of app.quit() > > seems to be the correct one for this case. > > Nope, I disagree! If you want your widget to remain visible but disabled, > then you should change its state to "disabled" but please leave the > mainloop alone. This app.destroy() I meant to call at app exit, not at disabling editPool. > > Is such use of nested mainloops considered an error? Is there a > > better way of doing this? > > Yes and yes. I am still not sure what exactly you want to achieve with > two mainloops, but you certainly don't need them. > If you need more than one toplevel window, use Toplevel() instead of a > second Tk(). There is no second Tk(). The editPool(Frame) just calls Frame.__init__() in __init__(). Then calls editPool.mainloop() in editPool.activate(). > If you want to temporarily hide a Tk or Toplevel window without > destroying it altogether, use withdraw(). If you want to temporarily hide > a non-toplevel widget, use pack/grid_forget(). > If you want a widget to remain visible but inactive, set its state to > "disabled" or "readonly", which ever fits best. If it is a custom > "mega-widget" (as the editpool appears to be), simply set all of its > subwidgets to state="disabled". As I said it disables it correctly, but uses mainloop() to handle the input. Without it it doesn't input the text in the paper after pressing Enter. Reinis From klappnase at web.de Sun Feb 7 07:45:13 2016 From: klappnase at web.de (Michael Lange) Date: Sun, 7 Feb 2016 13:45:13 +0100 Subject: [Tkinter-discuss] Debugging non-zero exit code In-Reply-To: <20160207073553.GI24608@RD-OC.homerouter.cpe> References: <20160205194404.GF24608@RD-OC.homerouter.cpe> <20160205210939.d59b1a0a9e9c7be3a6e59842@web.de> <20160206164307.GG24608@RD-OC.homerouter.cpe> <20160206200243.1d34a2b64aa35712ff5366c6@web.de> <20160206202238.GH24608@RD-OC.homerouter.cpe> <20160206231004.4e35e52dce0e1f63fad2e554@web.de> <20160207073553.GI24608@RD-OC.homerouter.cpe> Message-ID: <20160207134513.85c65f6c98d638f5c6d644c5@web.de> Hi, > As I said it disables it correctly, but uses mainloop() to handle > the input. Without it it doesn't input the text in the paper > after pressing Enter. it should certainly be possible to write a handler that does this without a second mainloop. Not sure what causes the problem, maybe it is similar to this example? from Tkinter import * root = Tk() e = Entry(root) e.pack(padx=100, pady=100) e.insert('end', 'foo') def test(event=None): print(e.get()) e.bind('', test) e.configure(state='disabled') root.mainloop() If yes, it might help to either change the Entry's state to "readonly" or to add a binding for the root window, as in root.bind('', test) I am aware that your program is way more complex than this tiny example, so setting this up might require considerable efforts, but it's certainly worth it. Multiple mainloops are definitely evil. Best regards Michael .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. A father doesn't destroy his children. -- Lt. Carolyn Palamas, "Who Mourns for Adonais?", stardate 3468.1. From bob at passcal.nmt.edu Wed Feb 10 18:30:18 2016 From: bob at passcal.nmt.edu (Bob Greschke) Date: Wed, 10 Feb 2016 16:30:18 -0700 Subject: [Tkinter-discuss] Transferring tags from one Text() to another Message-ID: <308460FA-137D-4D7C-B70E-CB9E38D835BB@passcal.nmt.edu> I write stuff to a Text() and some words and lines are different colors or are links (binds) using Tags. I can Text().get(0.0, END), get all of the text, then insert that in another Text() to make a copy, but how do I get/transfer the other "formatting" info? The binds aren't real important, but the different colored text would be nice. Thanks! Bob From klappnase at web.de Wed Feb 10 19:23:01 2016 From: klappnase at web.de (Michael Lange) Date: Thu, 11 Feb 2016 01:23:01 +0100 Subject: [Tkinter-discuss] Transferring tags from one Text() to another In-Reply-To: <308460FA-137D-4D7C-B70E-CB9E38D835BB@passcal.nmt.edu> References: <308460FA-137D-4D7C-B70E-CB9E38D835BB@passcal.nmt.edu> Message-ID: <20160211012301.5a7e0e42706f7eb28182d758@web.de> Hi, On Wed, 10 Feb 2016 16:30:18 -0700 Bob Greschke wrote: > I write stuff to a Text() and some words and lines are different colors > or are links (binds) using Tags. I can Text().get(0.0, END), get all of > the text, then insert that in another Text() to make a copy, but how do > I get/transfer the other "formatting" info? The binds aren't real > important, but the different colored text would be nice. I think you could apply the same calls to tag_configure() and tag_bind() to both widgets and then try something like this to copy the tags from one widget to the other (at least as a starting point): def copy_tags(): tags = t1.tag_names() for tag in tags: r = t1.tag_ranges(tag) if r: t2.tag_add(tag, *r) If the parts of text that are "tagged" are not at the same position in t2 you would of course have to shift each element of t1.tag_ranges() by an offset of t2.index.insert() or so. Best regards Michael .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. Murder is contrary to the laws of man and God. -- M-5 Computer, "The Ultimate Computer", stardate 4731.3 From ingoogni at gmail.com Sun Feb 14 17:13:57 2016 From: ingoogni at gmail.com (ingo) Date: Sun, 14 Feb 2016 23:13:57 +0100 Subject: [Tkinter-discuss] Transferring tags from one Text() to another In-Reply-To: <308460FA-137D-4D7C-B70E-CB9E38D835BB@passcal.nmt.edu> References: <308460FA-137D-4D7C-B70E-CB9E38D835BB@passcal.nmt.edu> Message-ID: Op 2016-02-11 om 00:30 schreef Bob Greschke: > I write stuff to a Text() and some words and lines are different colors or are links (binds) using Tags. I can Text().get(0.0, END), get all of the text, then insert that in another Text() to make a copy, but how do I get/transfer the other "formatting" info? The binds aren't real important, but the different colored text would be nice. > Not fully fleshed out, just an idea, I Text().dump() the selection and change the list so it can be reinserted easily. Currently not dealing with marks images etc. logger = logging.getLogger() logger.setLevel(logging.DEBUG) stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.DEBUG) logger.addHandler(stream_handler) def parse_dump(self, dump): """Parse the list, created by tkinter text dump()""" keystack = [] output = [] for section in dump: logger.debug("{0}".format(section)) key, tag, index = section if key == 'tagon': keystack.insert(0, tag) logger.debug("Insert Tag: {}".format(tag)) elif key == 'tagoff': try: keystack.pop(keystack.index(tag)) logger.debug("Pop Tag: {}".format(tag)) except ValueError as e: pass #!!!! elif key in ('window', 'image', 'mark'): pass else: #== 'text' output.append((tag, tuple(keystack))) logger.debug("KeyStack: {}".format(keystack)) return output def insert_dump(self, pdump): """Insert the parsed text dump at given position""" for line in pdump: logger.debug("{} |{}".format(line[0],line[1])) self.insert('end', line[0], line[1]) Ingo