3

I'm writing a Python program that is basically the Snipping Tool. I'd like to be able to run my program, select the area for my screenshot using my mouse to click and drag, and then have the program save this image.

I was trying it out with the code found here: http://pyscreenshot.readthedocs.io/en/latest/

#-- include('examples/showgrabfullscreen.py') --#
import pyscreenshot as ImageGrab

if __name__ == '__main__':

# grab fullscreen
im = ImageGrab.grab()

# save image file
im.save('screenshot.png')

# show image in a window
im.show()
#-#

(under "grab and show part of the screen"), but this doesnt let the user click and drag. Does anyone know how I could do this? I found some examples online but they're all hundreds of lines long and I don't think this simple program should be that long (but I could be wrong).

Thanks!

Brett Holmes
  • 397
  • 5
  • 20
ATP
  • 603
  • 1
  • 9
  • 14
  • Hi, welcome to SO, don't use a link alone in your post, include the code you want to show us. The link could go dead in a few months or years, which will make the question useless. Copy and paste the code, and include the link as a source ! :) – Alexandre Beaudet Apr 18 '18 at 14:13
  • Rather than trying to create a snipping tool from scratch, why not simply use a [sub-process](https://stackoverflow.com/questions/204017/how-do-i-execute-a-program-from-python-os-system-fails-due-to-spaces-in-path) to run the Windows Snipping Tool? – Christian Dean Apr 18 '18 at 14:14
  • 1
    Thank you both! If I run Snipping Tool using Python, will I be able to automatically save the picture? I want to avoid having to click on "Save", choosing the folder, and then saving it manually. – ATP Apr 18 '18 at 14:32

1 Answers1

5

I solved this problem by drawing on a transparent layer. This will allow the user to see through the invisible drawing layer and provide a surface for drawing the snipping box. When the user releases the snipping box functionality, it will destroy the invisible layer and capture the box coordinates. It will then take a screen shot within the captured coordinates and create + save this time stamped image to a directory called "snips" (feel free to adjust this).

from tkinter import *
import pyautogui

import datetime


def take_bounded_screenshot(x1, y1, x2, y2):
    image = pyautogui.screenshot(region=(x1, y1, x2, y2))
    file_name = datetime.datetime.now().strftime("%f")
    image.save("snips/" + file_name + ".png")


class Application():
    def __init__(self, master):
        self.snip_surface = None
        self.master = master
        self.start_x = None
        self.start_y = None
        self.current_x = None
        self.current_y = None

        root.geometry('400x50+200+200')  # set new geometry
        root.title('Lil Snippy')

        self.menu_frame = Frame(master)
        self.menu_frame.pack(fill=BOTH, expand=YES, padx=1, pady=1)

        self.buttonBar = Frame(self.menu_frame, bg="")
        self.buttonBar.pack()

        self.snipButton = Button(self.buttonBar, width=5, height=5, command=self.create_screen_canvas, background="green")
        self.snipButton.pack()

        self.master_screen = Toplevel(root)
        self.master_screen.withdraw()
        self.master_screen.attributes("-transparent", "maroon3")
        self.picture_frame = Frame(self.master_screen, background="maroon3")
        self.picture_frame.pack(fill=BOTH, expand=YES)

    def create_screen_canvas(self):
        self.master_screen.deiconify()
        root.withdraw()

        self.snip_surface = Canvas(self.picture_frame, cursor="cross", bg="grey11")
        self.snip_surface.pack(fill=BOTH, expand=YES)

        self.snip_surface.bind("<ButtonPress-1>", self.on_button_press)
        self.snip_surface.bind("<B1-Motion>", self.on_snip_drag)
        self.snip_surface.bind("<ButtonRelease-1>", self.on_button_release)

        self.master_screen.attributes('-fullscreen', True)
        self.master_screen.attributes('-alpha', .3)
        self.master_screen.lift()
        self.master_screen.attributes("-topmost", True)

    def on_button_release(self, event):
        self.display_rectangle_position()

        if self.start_x <= self.current_x and self.start_y <= self.current_y:
            print("right down")
            take_bounded_screenshot(self.start_x, self.start_y, self.current_x - self.start_x, self.current_y - self.start_y)

        elif self.start_x >= self.current_x and self.start_y <= self.current_y:
            print("left down")
            take_bounded_screenshot(self.current_x, self.start_y, self.start_x - self.current_x, self.current_y - self.start_y)

        elif self.start_x <= self.current_x and self.start_y >= self.current_y:
            print("right up")
            take_bounded_screenshot(self.start_x, self.current_y, self.current_x - self.start_x, self.start_y - self.current_y)

        elif self.start_x >= self.current_x and self.start_y >= self.current_y:
            print("left up")
            take_bounded_screenshot(self.current_x, self.current_y, self.start_x - self.current_x, self.start_y - self.current_y)

        self.exit_screenshot_mode()
        return event

    def exit_screenshot_mode(self):
        self.snip_surface.destroy()
        self.master_screen.withdraw()
        root.deiconify()

    def on_button_press(self, event):
        # save mouse drag start position
        self.start_x = self.snip_surface.canvasx(event.x)
        self.start_y = self.snip_surface.canvasy(event.y)
        self.snip_surface.create_rectangle(0, 0, 1, 1, outline='red', width=3, fill="maroon3")

    def on_snip_drag(self, event):
        self.current_x, self.current_y = (event.x, event.y)
        # expand rectangle as you drag the mouse
        self.snip_surface.coords(1, self.start_x, self.start_y, self.current_x, self.current_y)

    def display_rectangle_position(self):
        print(self.start_x)
        print(self.start_y)
        print(self.current_x)
        print(self.current_y)


if __name__ == '__main__':
    root = Tk()
    app = Application(root)
    root.mainloop()
Brett La Pierre
  • 493
  • 6
  • 15
  • 1
    I tried this but it apparently only works on one monitor: `pyautogui` doesn't support multiple monitors yet. Any other methods out there?? – RufusVS Apr 11 '21 at 21:03