7

UPDATED: SEE BELOW

I've been porting the code for this assignment: http://www.stanford.edu/class/cs221/progAssignments/PA1/search.html (the entire source code is available as zip from there) from Python 2.x to 3.x. Note, porting is not the assignment, that's just me trying to keep the code up to date and avoiding installing another version of Python...

After the usual 2.x -> 3.x syntax fixes (printing, exception raising, etc), and realizing that the module Tkinter is now known as tkinter in 3.x (lower-case), I've run into stranger problems, with this snippet and several others like it:

def keys_pressed(d_o_e=tkinter.tkinter.dooneevent,d_w=tkinter.tkinter.DONT_WAIT)

The errors are of the type:

AttributeError: 'module' object has no attribute 'tkinter'

Code completion in my IDE and the variable trace indeed seems to indicate that the tkinter module has no attribute or sub-class tkinter under which one might refer to dooneevent or DONT_WAIT. However, there are a few other references on the Internet of people using constructs like

_tkinter.dooneevent(_tkinter.DONT_WAIT)

to move the main loop ahead, but even referencing it like that still yields the same error.

Any ideas greatly appreciated.


UPDATE: Referring to the _root_window via lambda notation seems to work, as in it no longer complains pre-execution time in the majority of cases. To my untrained eye however, this is basically "magic", and as such I have little idea what this subsequent error is saying or how to work around it. The method now looks like this, with my changes in the first line:

def move_to(object, x, y=None, d_o_e=lambda arg: _root_window(arg), d_w=tkinter._tkinter.DONT_WAIT):
    if y is None:
        try: x, y = x
        except: raise  'incomprehensible coordinates'

    horiz = True
    newCoords = []
    current_x, current_y = _canvas.coords(object)[0:2] # first point
    for coord in  _canvas.coords(object):
      if horiz:
        inc = x - current_x
      else:
        inc = y - current_y
      horiz = not horiz

      newCoords.append(coord + inc)

    _canvas.coords(object, *newCoords)
    d_o_e(d_w)

and the error I get is:

TypeError: 'Tk' object is not callable              

referencing the line where the method is defined (first line above).

Mikael
  • 356
  • 2
  • 9
  • The Stanford link is dead and the code linked from there is not accessible. Hence I do not know what the name '_root_window', used in answers, actually refers too. – Terry Jan Reedy Feb 27 '16 at 07:07
  • The possibility of links going dead is why SO requests that questions have complete runnable code and that answers at least summarize links with answers. – Terry Jan Reedy Feb 27 '16 at 07:12
  • 1
    I think I found the answer. `tkinter.Tk().tk` is an instance of of a `_tkinter.tkapp` class that seems to be otherwise hidden. (I am guessing that it is a proxy for the tk app and cannot be instantiated other than by instantiating `tkinter.Tk`. The tkapp instance has the `.dooneevent` function (method) and others that were previously available in 2.x as `Tkinter.tkinter.xyz` (where `Tkinter.tkinter` was actually `_tkinter`). – Terry Jan Reedy Feb 27 '16 at 07:24

1 Answers1

10

It appears Tkinter.tkinter was changed to tkinter._tkinter in Python 3. Compare these docs from Python 2 with these from Python 3. Also, dooneevent is no longer in tkinter._tkinter, but is still a member of the Tk (aka root) object.

So change your code to

def keys_pressed(d_o_e=lambda arg: _root_window.dooneevent(arg),
        d_w=tkinter._tkinter.DONT_WAIT):

This takes advantage of the fact that in your linked code _root_window is global, so while _root_window is not available when the class is defined, it will be available when the lambda is run.

Steven Rumbalski
  • 44,786
  • 9
  • 89
  • 119
  • Yeah, that's what I tried already. (and I assume you mean it changed from `Tkinter.tkinter` to `tkinter._tkinter` - `Tkinter._tkinter` gives a `name Tkinter not defined` error). Could I be missing something in the way I'm importing this? I'm just doing `import tkinter`, although `from tkinter import *` doesn't seem to make a difference either. – Mikael Oct 10 '11 at 14:58
  • Odd. What are the results of `dir(tkinter)`, `dir(tkinter.tkinter)`, and `dir(tkinter._tkinter)`? – Steven Rumbalski Oct 10 '11 at 15:01
  • dir(tkinter._tkinter): ['ALL_EVENTS', 'DONT_WAIT', 'EXCEPTION', 'FILE_EVENTS', 'IDLE_EVENTS', 'READABLE', 'TCL_VERSION', 'TIMER_EVENTS', 'TK_VERSION', 'TclError', 'Tcl_Obj', 'TkappType', 'TkttType', 'WINDOW_EVENTS', 'WRITABLE', '__doc__', '__file__', '__name__', '__package__', '_flatten', 'create', 'getbusywaitinterval', 'setbusywaitinterval'] dir(tkinter.tkinter) gives the aforementioned `AttributeError: 'module' object has no attribute 'tkinter'` dir(tkinter) is very long, let me know if you want me to post it (assuming I have sufficient chars for it in comments). Thx – Mikael Oct 10 '11 at 15:10
  • I know you said you tried using `tkinter._tkinter` already, but I think you made a mistake when doing so. Based on what you posted from `dir(tkinter._tkinter)`, you should not get get any attribute errors when doing `def keys_pressed(d_o_e=tkinter._tkinter.dooneevent,d_w=tkinter._tkinter.DONT_WAIT):`. – Steven Rumbalski Oct 10 '11 at 15:15
  • However, I don't see a `tkinter._tkinter.dooneevent` in your dir of `tkinter._tkinter`. – Steven Rumbalski Oct 10 '11 at 15:18
  • That's right - it's just not there (double-checked again). This is CPython 3.2.2 Win32, by the way, if it matters, with TkVersion 8.5. – Mikael Oct 10 '11 at 15:55
  • 1
    Your `_root_window` object should have a `dooneevent` method. Unfortunately, this is not available when the function is created. Since `_root_window` is a `global` you can do this instead: `keys_pressed(d_o_e=lambda arg: _root_window(arg), d_w=tkinter._tkinter.DONT_WAIT)`. This way `_root_window` is looked up when the lambda is called, not when the function is created. – Steven Rumbalski Oct 10 '11 at 17:15
  • Hi Steven, yes that seemed to do the trick, but ran into other problems possibly as a side effect: `TypeError: 'Tk' object is not callable`. I've updated the problem description above. Thanks for your help so far! – Mikael Oct 10 '11 at 18:14
  • 1
    oops. should have written `_root_window.dooneevent(arg)` instead of `_root_window(arg)`. – Steven Rumbalski Oct 10 '11 at 18:18
  • Thanks again Steven, that did it (and makes more sense to me now as well!) Still some issues elsewhere in the codebase preventing it from executing but that seems to be unrelated to tkinter. Marked as answered, thanks again. :) – Mikael Oct 10 '11 at 23:08
  • What is the '_root_window' object? The link to the code that defines it is dead. On Windows running 3.5.1, `tkinter.Tk` and `tkinter.Tk()`, which is what I thought it might be, do not have .dooneevent. – Terry Jan Reedy Feb 27 '16 at 07:11
  • I found the answer and added it as a comment to the question. – Terry Jan Reedy Feb 27 '16 at 07:24