Python package issue

I’ve got a script that currently is working, it grabs a screenshot of the active view but I am wanting to change that to allow the user to grab their own screenshot.

I got a python script working in Pycharm, but one python package is giving me issues.
The package is “pyautogui”.
I tried installing the same way as I installed other packages, but it still says “No module named 'pyautogui”.

This is my python script that I got working in Pycharm:

import pyautogui
import os
import tkinter as tk


# Function to select a region
def select_region():
    global x_start, y_start, x_end, y_end, root, canvas
    root = tk.Tk()
    root.attributes('-fullscreen', True)
    root.attributes('-alpha', 0.3)
    root.configure(background='gray')

    # Create a canvas for drawing the selection rectangle
    canvas = tk.Canvas(root, cursor="cross", bg="gray", highlightthickness=0)
    canvas.pack(fill=tk.BOTH, expand=True)

    root.bind('<Button-1>', start_selection)
    root.bind('<B1-Motion>', draw_selection)
    root.bind('<ButtonRelease-1>', end_selection)
    root.mainloop()


# Start region selection
def start_selection(event):
    global x_start, y_start, rect
    x_start, y_start = event.x, event.y
    rect = canvas.create_rectangle(x_start, y_start, x_start, y_start, outline='red', width=2)


# Draw selection rectangle
def draw_selection(event):
    global rect
    canvas.coords(rect, x_start, y_start, event.x, event.y)


# End region selection
def end_selection(event):
    global x_start, y_start, x_end, y_end, root
    x_end, y_end = event.x, event.y
    root.destroy()


def capture_region(save_path):
    try:
        print("Drag to select a region of your screen...")
        select_region()
        x1, y1, x2, y2 = min(x_start, x_end), min(y_start, y_end), max(x_start, x_end), max(y_start, y_end)
        region = pyautogui.screenshot(region=(x1, y1, x2 - x1, y2 - y1))
        region.save(save_path)
        return f"Screenshot saved to {save_path}"
    except Exception as e:
        return f"Error: {str(e)}"


if __name__ == "__main__":
    # Specify the save path on your desktop
    save_path = r"C:\Users\ian\Desktop\test\screenshot.png"

    # Ensure the directory exists
    os.makedirs(os.path.dirname(save_path), exist_ok=True)

    # Capture the region and save the screenshot
    result = capture_region(save_path)
    print(result)

Any suggestions on fixes or maybe a different pacakge to use for letting the user get a screeshot?

Are you seeing a warning in Dynamo only or others as well? What build of Dynamo and Python engine are you using?

Yeah, sorry about that, probably need all that reference information.

Revit 2025.4
Dynamo 3.3
Python 3.12.3
CPython3

This is the error in Dynamo

No errors in pycharm

Any better luck with the new PythonNET3 package? (Info here: PythonNet3: A New Python to Fix Everything - Dynamo BIM)

I’m not seeing the option to switch to PythonNet3


I confirmed I am using Dynamo 3.3

If you just need the revit canvas, try this maybe… no dependencies:

So this is what I currently do, which seems similar, and it works:


The issue is that if there are in model space, or any view that is expansive then it grabs the entire view which is normally useless.

I figured it would be more intuitive for the user to be able to grab their own screenshot so they could capture exactly what they wanted.

From here I’m uploading the image to Imgur so I can get an URL which is what I need to upload the image to Notion to put into the Page I’m creating via this workflow.

1 Like

@kslifter Just to confirm, you have installed the PythonNet3 Engine package from Package manager?

1 Like

wow I feel dumb, lol, I hadn’t installed it.

I ran it switched, but still go a similar error

I won’t be able to get to it till tomorrow, but I might try some other avenues suggested in this link:

It looks like I’m not the only user having issues with this package in Dynamo->

Quick and dirty using .net - works with CPython3 and PythonNet3 no dependencies
Takes a full screenshot of the primary screen

from System.Drawing import Bitmap, CopyPixelOperation, Graphics
from System.Windows.Forms import Screen

def screenshot():
    s = Bitmap(
        Screen.PrimaryScreen.Bounds.Width,
        Screen.PrimaryScreen.Bounds.Height
    )
    
    g = Graphics.FromImage(s)
    
    g.CopyFromScreen(
        Screen.PrimaryScreen.Bounds.X,
        Screen.PrimaryScreen.Bounds.Y,
        0,
        0,
        s.Size,
        CopyPixelOperation.SourceCopy
    )
    
    return s

OUT = screenshot()

5 Likes

Hi

a similar solution
screenshot_custom

import sys
import clr
import System

# Add references to required .NET assemblies
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System import IntPtr
from System.Drawing import Bitmap, Graphics, Point, Rectangle, Size, Color
from System.Windows.Forms import (
    Application, Button, Clipboard, Cursor, Form, Keys, Label, MouseButtons, Panel
)

class CaptureForm(Form):
    """ Full-Screen Transparent Form for Screenshot Selection """
    
    def __init__(self):
        super().__init__()
        self.screenshot = None
        self.InitializeComponent()
        
    def InitializeComponent(self):
        self.capture_button2 = Button()
        self.SuspendLayout()
        #
        # capture button
        #
        self.capture_button2 = Button()
        self.capture_button2.Text = "Screenshot"
        self.capture_button2.Width = 150
        self.capture_button2.Top = 5
        self.capture_button2.Left = 5
        self.capture_button2.BackColor = System.Drawing.Color.Gray  
        self.capture_button2.ForeColor = System.Drawing.Color.White 
        self.capture_button2.Click += self.CaptureClick
        self.Controls.Add(self.capture_button2)
        # 
        # MainForm
        # 
        self.Opacity = 0.85 
        self.TopMost = True
        self.BackColor = System.Drawing.Color.Black 
        self.TransparencyKey = System.Drawing.Color.Black 
        #self.FormBorderStyle = getattr(System.Windows.Forms.FormBorderStyle., "None")
        self.Text = "Screenshot Capture"
        self.ResumeLayout(False)
        

    def CaptureClick(self, sender, e):
        """ Capture selected screen area when mouse button is released """
        self.CaptureScreen()
        self.Close()
            

    def CaptureScreen(self):
        """ Captures the selected screen area """
       
        y1 = self.Top
        x1 = self.Left
        height = self.Height
        width = self.Width
        x2 = x1 + width
        y2 = y1 + height

        if width == 0 or height == 0:
            return  # avoid capturing empty selection
        self.Opacity = 0
        # create a new bitmap with the selected area dimensions
        screenshot = Bitmap(width, height)
        graphics = Graphics.FromImage(screenshot)

        # capture the screen
        graphics.CopyFromScreen(
            min(x1, x2), min(y1, y2),  # source x, y
            0, 0,                      # destination x, y
            screenshot.Size            # size of the capture
        )
        self.screenshot = screenshot

# Create and display the form
form = CaptureForm()
form.ShowDialog()
OUT = form.screenshot

based on this project

4 Likes

Hi all, the python distribution that we include with Dynamo is not a full python.
Some packages might have dependencies on things from a full python install.
In this case pyautogui depends on Lib\socket.py ( and probably other needed modules ) which are present in a full python install but not in our light python we distribute with Dynamo.

2 Likes

A workaround to the situation Bogdan describes above is to force Dynamo to use your own Python installation. Any Python version the same or greater than the default (CPython: 3.9.12, PythonNet3: 3.11.0) will likely work, though the newer you get the more likely you’ll see some minor differences from the default.

  • In PythonNet3, there are environment variables you can use to control which Python installation to use¹:
    • DYN_DISABLE_PYTHONDLL_OVERRIDE = “yup” (any text here, just make it non-blank) Setting this variable removes the Dynamo package’s override and allows the included PythonNet to look for and use this :point_down:
    • PYTHONNET_PYDLL = , e.g., on my system, “C:\Users\wastvet.pyenv\pyenv-win\versions\3.12.5\python312.dll”
  • In CPython3 no such overrides exist. But, you can just replace the python folder that Dynamo creates with your own (you could do this with PythonNet3 too, but environment variables give you more control)
    • Empty out the folder %localAppData%\python-3.11.0-embed-amd64 and copy in the contents of another python installation (in command prompt type which python to see where your default python gets run from).

1 Set environment variables by:

  • <win key>, type “environment variables”, select “Edit the environment variables…”
  • Click “Environment Variables” button at the bottom
  • Click “New…” under the User variables section for each variable to add
6 Likes

This worked great, thanks for the help!

2 Likes