0

I'm making a little program in Python using tkinter to make multiple objects move.

First I tried Threading to create 2 objects and make them move at the same time. So I copy/paste the function that create and move 1 object and then execute the two functions in 2 threads.

Now I wanted to try to choose the number of object moving so I got 1 function and I want to execute it multiple times at the same time. Threading was no longer a possibility.

So I tried multiprocessing and I got a problem : it does nothing... I recreated the problem with a simple code :

import multiprocessing

def pr(number):
    print(number)

p = multiprocessing.Process(target=pr,args=(40,))
p.start()

It returns an absolute nothing, nothing is printed. I think multiprocessing is not the adequate tool to do what I want but I don't understand the problem of this code.

PS : I'm using IDLE Python 3.7.2 on Windows

LeoZ
  • 13
  • 5
  • Looks fine to me. When I run that code, it prints 40 as intended. – Silvio Mayolo Mar 01 '22 at 14:33
  • What is your platform (you are supposed to tag your question with this)? Linux? Windows? Your own OS that you wrote? What environment? Command prompt? Jupyter Notebook? Idle? iPython? Something you wrote? And what do you mean it's not working? No output? It hangs? An error message? A stacktrace? – Booboo Mar 01 '22 at 15:02
  • "it's just not working" isn't helpful, as it's just your interpretation What did you observe? What did you expect instead? – Ulrich Eckhardt Mar 01 '22 at 15:09
  • @Booboo It doesn't work, I mean, for the example code, it prints nothing, the print command is not executed. It does nothing. I'm using Windows and IDLE Python 3.7.2 – LeoZ Mar 01 '22 at 15:31
  • @UlrichEckhardt This is supposed to print "40" but instead I have nothing, no error, no print – LeoZ Mar 01 '22 at 15:34
  • Does the program terminate? If I take your response verbatim, it doesn't even do that but sits there. But even that is /something/, so please describe that at least! Further, what about when you add outputs at various places in the code? What if you write to a file? There are many ways to find out if code is called even when `print()` doesn't have any visible effect. Oh, and you could eliminate IDLE from the calculation as well and run Python from the commandline. – Ulrich Eckhardt Mar 01 '22 at 15:46
  • 1
    You can't run multiprocessing code under IDLE. Put this code in a file, such as *test.py*, and then execute *python test.py* from a command line. And you need to put the code that creates and starts the process in a block: `if __name__ == '__main__':` – Booboo Mar 01 '22 at 15:54
  • @Booboo multiprocessing itself works fine from IDLE when that block is included. LeoZ's problem is trying to print to a default stdout=None. (And using multiprocessing when it is not the best solution.) See my answer. – Terry Jan Reedy Mar 02 '22 at 05:14

2 Answers2

0

This post is essentially a duplicate of Can multiprocessing Process class be run from IDLE. I found it by searching SO for "[python-idle] multiprocessing". I give a revised and expanded version of my previous answer here.

  1. As @Booboo said, an if __name __ == '__main__': block is needed, at least on Windows and perhaps macOS. With that present, multiprocessing itself runs fine when started from IDLE on Windows.

  2. The failure is with printing. When multiprocessing starts a new python process, it copies the current process sys.__stdout__ to both sys.__stdout__ and sys.stdout in the new process. By default, print writes the requested text to sys.stdout.

When a GUI program is started from an icon or somehow other than a terminal, sys.__stdout__ and sys.stdout are initialized to None, at least on Windows. IDLE replaces sys.stdout with an object whose write method sends text to IDLE's Shell. It would be useless to copy the IDLE specific sys.stdout as it would not work.

If, instead you start IDLE from a command line with, on Windows, py -m idlelib, the sys attributes are instead initialized to a normal python io object that sends text to the console/terminal. IDLE only replaces sys.stdout, so multiprocessing can copy the working sys.__stdout__ io object to both sys attributes of the new process. By default, 'print' will then print to the console, as in this example.

import multiprocessing, sys

def pr():
    print(sys.stdout)

if __name__ == '__main__':
    p = multiprocessing.Process(target=pr)
    p.start()
    p.join()

# Writes the following to the 
<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>

Printing to CommandPrompt is obviously not great, but it is the best you can do with non-IDLE code. In your particular case, of wanting to animate tkinter objects (presumably on a tkinter canvas), you should use tk/tkinter after loops instead of multiprocessing. IDLE is designed for developing tkinter programs. Searching SO for [tkinter] animate gives a few hundred hits.

Terry Jan Reedy
  • 18,414
  • 3
  • 40
  • 52
  • Two things. *py* is a special Python launcher program available on Windows to manage multiple versions of Python (e.g. Python2, Python3) and not everyone has it installed. Moreover, now that I know that IDLE *can* run multiprocessing (thanks for the info), it cannot, however, as in your demo. As with Jupyter Notebook, worker functions such as `pr` must be imported from another module or else you get the following error: **AttributeError: Can't get attribute 'pr' on **. – Booboo Mar 02 '22 at 11:12
  • I have also discovered that it doesn't matter how you start IDLE. That is, you can start IDLE from the Windows start menu and the program will still work and `sys,stdout` prints out as **** even when started your way. It's just that if you don't start it your way, that is, from a command prompt, then there is no way of seeing the above **AtributeError** exception that occurs when you do not import the worker function, So the real problem just boils down to needing to import worker functions for use in multiprocessing and ... – Booboo Mar 02 '22 at 11:32
  • *python -m idlelib* from a command prompt is the best way to start IDLE so that you see all possible exceptions that might otherwise go unnoticed. – Booboo Mar 02 '22 at 11:34
0

My comment about not being able to run multiprocessing under IDLE was clearly wrong as pointed out by Terry Jan Reedy.

The problem with IDLE is the same problem one has in trying to do multiprocessing from an environment such as Jupyter Notebook. That is, it can be done except that your worker function, pr in your case, must be in a separate module that is imported. If you do not do this, then you get the following exception:

AttributeError: Can't get attribute 'pr' on <module '__main__' (built-in)>

Unfortunately, by starting IDLE from the Windows start menu, that exception message was never seen. Had IDLE been started from a command prompt instead with ...

python -m idlelib

... then the AttributeError exception would have been displayed on your command prompt console.

So:

  1. Place your pr function in a file, for example pr.py, in some directory.
  2. Open up a command prompt and change the current directory to the directory that contains pr.py.
  3. Start IDLE with python -m idlelib.

Then your program becomes:

import multiprocessing
from pr import pr


if __name__ == '__main__': # required for Windows
    p = multiprocessing.Process(target=pr,args=(40,))
    p.start()
    p.join() # Wait for the completion explicitly
Booboo
  • 38,656
  • 3
  • 37
  • 60