2

EDIT 1 - added more code I'm not sure that proc.communicate was needed, it was one of the suggestions I found from some other stackoverflow code.(Sorry I was tired last night and didn't think too much before asking the question.)

I should add that I am not an experienced coder (mechanical engineer) as you can probably tell from my code

In my Gui I have a button to call a subprocess

The subprocess (screenshot-cmd.exe) creates a png of a cropped screen shot but it won't actually produce the file until there is an error or if the button click event is over.

This makes me think that the subprocess is not actually run until the event is finished

I want to call the process several times after a single button press and move the files that it produces after each one is produced

if I use proc.wait(), the process hangs indefinitely.

How do I stop this?

# function to take a single image called 'fileName' and place it in directory 'dir'
def takeImage(dir,fileName):

    # calculate the view to capture to get the whole display window in.
    clientRect = win32gui.GetClientRect(win32gui.GetForegroundWindow())
    windowRect = win32gui.GetWindowRect(win32gui.GetForegroundWindow())
    print(windowRect)
    windowSize = [windowRect[2]-windowRect[0],windowRect[3]-windowRect[1]]
    print(windowSize)
    print(clientRect)

    diffSize = [windowSize[0] -clientRect[2], windowSize[1] - clientRect[3]]
    lrbBorder = diffSize[0]/2
    topBorder = diffSize[1] - lrbBorder

    print("sizeDiff = " + str(diffSize))
    windowName = win32gui.GetWindowText(win32gui.GetForegroundWindow())
    handleId = win32gui.GetForegroundWindow()


    leftMar = designLabel.GetPosition()[0] + lrbBorder
    topMar = designLabel.GetPosition()[1]  + topBorder + designLabel.GetSize()[1]
    rightMar = leftMar + scene.width
    bottMar = topMar+scene.height

    margins = [leftMar,topMar,rightMar,bottMar]

    print(margins)

    # now print the view.
    #command_line = r"screenshot-cmd -wt '" + windowName + "' -rc " + str(margins[0]) + " " + str(margins[1]) + " " + str(margins[2]) + " " + str(margins[3]) + " -o " + fileName

    command_line = r"screenshot-cmd -wt '" + windowName + "' -rc " + str(margins[0]) + " " + str(margins[1]) + " " + str(margins[2]) + " " + str(margins[3]) + " -o " + fileName

    print(command_line)
    args = shlex.split(command_line)
    proc = subprocess.Popen(args)
    proc.wait() 

    wx.Yield()


    if not os.path.isdir(dir):
        os.makedirs(dir)

    newPath = os.path.join(dir,fileName)

    if os.path.exists(newPath):
        os.remove(newPath)

    oldPath = os.path.join(os.getcwd(), fileName)
    print("Old Path: " + oldPath)
    print("Exists: " + str(os.path.exists(oldPath)))
    shutil.move(oldPath,newPath)

    return

#event called upon clicking 'takeTenImag' button
def takeTenImgE(evt):
    global designNo
    global workingDirectory
    global numDesigns
    fileNameRoot = "test_"
    fileExtention = ".png"

    # check there are at least 10 designs
    if numDesigns > 9 and os.path.exists(workingDirectory):
        # find directory path to put images in 
        dir = os.path.join(workingDirectory, "images")
        # for each design

        for x in range(10):
            print("design =" + str(designNo))
            fileName = fileNameRoot + str(designNo) + fileExtention
            print("------------------")
            print("for x = " + str(x) + " "  + fileName)
            #   create image and save
            print(dir)
            takeImage(dir,fileName)
            #move to next design

            wx.PostEvent(forwardDesign, wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, forwardDesign.GetId()) ) 
            wx.Yield()
            print("design =" + str(designNo))
return

takeTenImg = wx.Button(p, label='Take Ten Images', pos=(rb + visScaleText.GetSize()[0]+10,takeImg.GetPosition()[1]+5 +takeImg.GetSize()[1]), size = (100,30))
takeTenImg.Bind(wx.EVT_BUTTON, takeTenImgE)

https://code.google.com/p/screenshot-cmd/

Holloway
  • 6,412
  • 1
  • 26
  • 33
  • Please post the complete event handling callback implementation. Your proc.communicate() is a blocking call is it required ? Also how are u handling multiple events not updating your subprocess.Popen returned object. Does ur command require a shell and why did u comment out the previous calls with the subprocess.PIPE objects ? – cmidi Mar 17 '15 at 00:46
  • I've edited the code, I'm not sure what you mean by "multiple events not updating your subprocess.Popen returned object. " or how to tell if my codde requires a shell. I tried shell=True and False and neither worked. The commented lines were due to previous attempts to fix the code, I've cleaned them up now – Barnaby Lewis Mar 17 '15 at 09:48
  • `Popen.wait()` and `Popen.communicate()` are both blocking calls it will cause your program to wait until the child process terminates or has an error. if you dont want that you should use `Popen.poll()` it will return ``None` if the process is running, to check the status of the child process but for that to work you would have to store all the Popen objects in a container like a list so that you dont over write the object with the newly created object when you handle the click event multiple times – cmidi Mar 17 '15 at 15:00
  • Maybe I wasn't clear. I want to wait for the child process to finish. It should produce a png file that I then want to move. But, the child process doesn't appear to start until the button click event is finished. If I just do a single call and put a time.sleep(10) line AFTER takeImage(), the image doesn't appear for 10 seconds. If I don't have the sleep statement, the image is generated shortly after the click event finishes. If I use Popen.wait() the image never appears. – Barnaby Lewis Mar 17 '15 at 16:11

2 Answers2

0

Barnaby, you may be over-complicating your subprocess use. Popen is typically used for when you need to communicate with the process during the time it is running. From the sound of it, you don't need to do that, so might want to use a higher level function. See the docs on subprocess' various invocations, and perhaps try using the call method. You'll need shell=True, as detailed in SO questions such as this one.

Drum
  • 428
  • 3
  • 8
0

I've found that the error is in my calling of subprocess.

I was using:

command_line = r"screenshot-cmd -wt '" + windowName + ...." 
args = shlex.split(command_line)
subprocess.call(args,shell=True)

changing this to:

command_line = r"screenshot-cmd -wt '" + windowName + ...." 
subprocess.call(command_line,shell=True)

solves the hang.

What is peculiar is that both options work when not inside a wx button click event(i.e. a python script launched from the command line), but only the second works when inside the wx button click event. If anyone could enlighten me why that would be most appreciated.

EDIT: upon further investigation, the hang is caused by trying to specify the active window in screenshot-cmd.

To solve this I find the position of the window using windowRect = win32gui.GetWindowRect(win32gui.GetForegroundWindow())

and then use screenshot-cmd without specifying a window.

This solves all issues although it is unclear why this causes problems