pyQT Designer ? in Dynamo

Hello,

does anyone use it for dynamo ?

is there also implementation tutorials regarding that ?

KR
Andreas

Normally, if developer want to integrate with user interface, they will choose zero touch node.
I recommend if you want to save your time for that, let go with C# + WPF , Python not is everything for any case .

3 Likes

For me i want all my scripts not to have dependency on anything (not even python libraries that are not OOTB)

(Im frothing for numpy etc…)

I built on the below to create forms where rquired for my user inputs.

2 Likes

@pyXam @chuongmep ,

i think it is even not supported

import sys
import clr

import numpy
from numpy import *

import pandas
from pandas import *

#import PyQt6
#from PyQt6 import *


OUT = sys.version, pandas.__version__, numpy.__version__


So what is than recomandable ? PyRevit ? or the hard way C# :wink:

KR

Andreas

Yeah its not supported yet…

Im so excited for when it is.

The link i gave is for winforms which is supported and has a python script that has a few examples already built into it.

It takes a bit of time to set up the first one and understand it but after that its pretty easy to modify for each scenario.

1 Like

@pyXam ,

here also some errors:

#thanks to Ben Robinson

import clr
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)

#############-------------\-------------##############

        ### UI additional references ###
        
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, FormWindowState, Screen, Label, PictureBox, PictureBoxSizeMode, AnchorStyles, BorderStyle, ComboBox, ComboBoxStyle, FormBorderStyle, CheckBox, TextBox, TextBoxBase
from System.Windows.Forms import Button, LinkLabel, Panel, Button
from System.Drawing import Icon, Color, Font, Point, Size

#system location C:\Windows\Microsoft.NET\Framework64\v4.0.30319
import System.IO

#############-------------\-------------#############

        ### Define some variables ###

these variables are static through the 2 windows
bitmapImage = System.Drawing.Bitmap("C:\Users\andre\Downloads\\" + "icon.png") #titlebar logo as bmp
titleIcon = Icon.FromHandle(bitmapImage.GetHicon()) #titlebar logo as icon
titleText = "Dimensioning"      #text that appears in the GUI titlebar
btnHeight = 40 #declared here as used for btn locations
btnWidth = 120
spacing = 20    #spacing size for GUI elements to form a consistent border
fontMessage = Font("Helvetica ", 9)
fontCK = Font("Helvetica ", 8) #set Checkbox Font
winSize = Size(1000,600) #consistant window size


#############-------------\-------------#############

        ### Global defs ###
        
def button(txt, loc, clc):
    btn = Button()
    #btnCancel.Parent = self
    btn.Text = txt
    btn.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right)
    btn.Location = loc
    btn.Click += clc
    btn.Height = btnHeight
    btn.Width = btnWidth
    btn.BackColor = Color.FromArgb(220, 220, 220)
    return btn

def logo(uiW, uiH):
    logo = PictureBox()
    bitmapImage = System.Drawing.Bitmap("C:\Me\Dynamo\WPF in Dynamo\\" + "dyna-sco-logo-text.png")              
    logo.Image = bitmapImage
    ratio =  float(logo.Height)/ float(logo.Width)  #needs to be a float as int will round to the nearest whole number
    logo.Size = Size(220, 130*ratio) #fixed for consistancy
    logo.Location = Point(spacing, (uiH - logo.Height)-spacing)
    logo.SizeMode = PictureBoxSizeMode.Zoom     # zooms the image to fit the extent
    logo.Anchor = (AnchorStyles.Bottom | AnchorStyles.Left)      #anchor styles lock elements to a given corner of the GUI if you allow users change size
    return logo


##########################-------------\-------------##########################

        ### Create a Class to define the first window ###
        
# create a instance of the form class called dimsionInputs.
#In Winforms, any window or a dialog is a Form.# 

class dimsionInputs(Form):       

    def __init__(self): #the __init__ method inside a class is its constructor

        self.Text = titleText             
        self.Icon = titleIcon
        self.BackColor = Color.FromArgb(255, 255, 255)  
        self.WindowState = FormWindowState.Normal # set maximised minimised or normal size GUI
        self.CenterToScreen() #centres GUI to the middle of your screen 
        self.BringToFront() #brings the GUI to the front of all open windows
        self.Topmost = True #true to display the GUI infront of any other active forms
        self.Size = winSize #small enough to fit on most screens
        uiWidth = self.DisplayRectangle.Width    #get the size of the form to use to scale form elements
        uiHeight = self.DisplayRectangle.Height
        stHeight = uiHeight / 4.5 #the height at which the check & text boxes will start
        htSpacing = 50 #a standard spacing vertical gap betwen check & text boxes    
        self.FormBorderStyle = FormBorderStyle.FixedDialog      # fixed dialog stops the user from adjusting the form size. Recomended disabling this when testing to see if elements are in the wrong place.#
        
        ### Set Default Values ###
        
#        self.dropDownOutput = 'pick one'
        
        self.txtBoxOffOutput = '500'  #offset of dimension from wall
        self.txtBoxCoordOutput = 'Internal' #name of coordinate system to be used
        self.ckBoxESOutput = False  #External Side?
        self.ckBoxLWOutput = False  #Linked Walls?
        self.ckBoxAWOutput = False  #All Walls?
        self.ckBoxMDOutput = False  #Multiple dims per wall?
        self.messText = 'test'
        self.runNextOutput = False  #how we decide if we want the next window to fire#
        self.Controls.Add(logo(uiWidth, uiHeight)) #add logo
        
#############-------------\-------------#############

        ### Create Main Window Text ###
        
        userMessage = Label()   #label displays texts
        font = fontMessage
        userMessage.Text = 'Automatic Dimensions for Linked or Project Walls'
        userMessage.Font = font
        userMessage.Location = Point(spacing, spacing)  #all location require a point object from system.Drawing to set the location.
        userMessage.Size = Size(uiWidth-(spacing*2),(uiHeight/4)-60)   #size the control with the width of the GUI to ensure it scales with different screen
        self.Controls.Add(userMessage)       #this adds control element to the GUI

#############-------------\-------------#############

        #combox drop down
#        cBox = ComboBox()   #dropdown control form
#        cBox.Location = Point(spacing,uiHeight/3)       
#        cBox.Width = uiWidth -(spacing*2)
#        cBox.Items.AddRange(tuple[(1,2,3,]))    # Adds an array of items to the list of items for a ComboBox.
#        cBox.DropDownStyle = ComboBoxStyle.DropDownList     #setting to dropdown list prevents users from being able to add aditional text values
#        cBox.SelectedIndexChanged += self.dropDownOutput  #.Click+= registers the press of the button to register the event handler and determine what action takes place when button clicked
#        self.Controls.Add(cBox)   

#############-------------\-------------#############

        ### CHECKBOXES ##
              
        #checkbox External Side
        ckBoxES = CheckBox()
        ckBoxES.Name="External Side"  
        ckBoxES.Text="External Side? (or internal side)"
        ckBoxES.Location = Point(spacing,stHeight)       
        ckBoxES.Width = uiWidth -(spacing*2)
        ckBoxES.Font = fontCK
        ckBoxES.Height = 40   
        ckBoxES.CheckStateChanged += self.ckBoxESChecked

        #checkbox Linked Walls 
        ckBoxLW = CheckBox()
        ckBoxLW.Name="Linked Walls"  
        ckBoxLW.Text="Linked Walls?"
        ckBoxLW.Location = Point(spacing,stHeight+htSpacing)       
        ckBoxLW.Width = uiWidth -(spacing*2)
        ckBoxLW.Font = fontCK
        ckBoxLW.Height = 40
        ckBoxLW.CheckStateChanged += self.ckBoxLWChecked

        
        #checkbox All Walls
        ckBoxAW = CheckBox()
        ckBoxAW.Name="All Walls"  
        ckBoxAW.Text="All Walls?"
        ckBoxAW.Location = Point(spacing,(stHeight+htSpacing*2))       
        ckBoxAW.Width = uiWidth -(spacing*2)
        ckBoxAW.Height = 40
        ckBoxAW.Font = fontCK
        ckBoxAW.CheckStateChanged += self.ckBoxAWChecked
        
        #checkbox MultiDims
        ckBoxMD = CheckBox()
        ckBoxMD.Name="Multi-Dims"  
        ckBoxMD.Text="Multi-Dims for each wall (for brick checking)"
        ckBoxMD.Location = Point(spacing,(stHeight+htSpacing*3))       
        ckBoxMD.Width = uiWidth -(spacing*2)
        ckBoxMD.Height = 40
        ckBoxMD.Font = fontCK
        ckBoxMD.CheckStateChanged += self.ckBoxMDChecked
        
        #add to Window
        self.Controls.Add(ckBoxES)
        self.Controls.Add(ckBoxLW)
        self.Controls.Add(ckBoxAW) 
        self.Controls.Add(ckBoxMD)  

#############-------------\-------------#############

        ### TEXTBOXES ##
     
        #TextBox Coordinate System      
        textBoxCoord = TextBox()
        textBoxCoord.Width = 300
        textBoxCoord.Text="Internal"
        textBoxCoord.Font = fontCK
        textBoxCoord.Location = Point(spacing + 400 ,(stHeight+htSpacing*4))
        textBoxCoord.TextChanged += self.txtBoxCoordChanged
        
        #TextBox Coordinate System - Label
        textBoxCoordLabel = Label()   #label displays texts
        textBoxCoordLabel.Text = "Coordinate System Name"
        textBoxCoordLabel.Width = 400
        textBoxCoordLabel.Font = fontCK
        textBoxCoordLabel.Location = Point(spacing,(stHeight+htSpacing*4))
        textBoxCoordLabel.Height = 50
        
        #add to Window
        self.Controls.Add(textBoxCoordLabel)  
        self.Controls.Add(textBoxCoord)
  
  
        
        #TextBox Offset        
        textBoxOffset = TextBox()
        textBoxOffset.Location = Point(spacing + 400,(stHeight+htSpacing*5))       
        textBoxOffset.Width = 300
        textBoxOffset.Text= "500"
        textBoxOffset.Name="Offset Distance"  
        textBoxOffset.Font = fontCK
        textBoxOffset.TextChanged += self.txtBoxOffChanged

               
        #TextBox Offset - Label
        textBoxOffsetLabel = Label()   #label displays texts
        textBoxOffsetLabel.Text = "Offset Distance"
        textBoxOffsetLabel.Width = 200
        textBoxOffsetLabel.Font = fontCK
        textBoxOffsetLabel.Height = 40
        textBoxOffsetLabel.Location = Point(spacing,(stHeight+htSpacing*5))
        #this is how you would do a margin
        #textBoxOffsetLabel.Margin = Padding(712,418,0,0) 
        
        #add to Window
        self.Controls.Add(textBoxOffsetLabel)  
        self.Controls.Add(textBoxOffset)
        
#############-------------\-------------#############   

        ### Buttons ## 

        #Create Ok Button
        btnOkClick = self.okButtonPressed #register click by user
        btnOkLoc = Point(uiWidth - ((btnWidth * 2) + spacing + 30), uiHeight - (btnHeight + spacing))
        btnOk = button('OK', btnOkLoc, btnOkClick) 
        
        #Create Cancel Button
        btnCnclClick = self.CnlButtonPressed #register click by user
        btnCnclLoc = Point(uiWidth - (btnHeight + spacing + 90), uiHeight - (btnHeight + spacing ))
        btnCancel = button('Cancel', btnCnclLoc, btnCnclClick)
        
        #add to Window
        self.Controls.Add(btnOk)
        self.Controls.Add(btnCancel)   
        
#############-------------\-------------#############

        ### Return Info From User ##

#these defs are the mechanism by which we assess the info from the user
#self is the instance of the GUI form. Sender is the control/widget. args is the argument/event provided from the control        

    ###dropDown ####
#    def dropDownOutput(self, sender, args):
#        self.dropDownOutput = sender.SelectedItem   #output the selected item.

    ### checkBoxes ###    
    def ckBoxESChecked(self, sender, args):
        if sender.Checked:
            self.ckBoxESOutput = True
        #otherwise indeterminate states give erroneous results
        else:
            self.ckBoxESOutput = False 
            
    def ckBoxLWChecked(self, sender, args):
        if sender.Checked:
            self.ckBoxLWOutput = True
        else:
            self.ckBoxLWOutput = False
    
    def ckBoxAWChecked(self, sender, args):
        if sender.Checked:
            self.ckBoxAWOutput = True
        else:
            self.ckBoxAWOutput = False
    
    def ckBoxMDChecked(self, sender, args):
        if sender.Checked:
            self.ckBoxMDOutput = True
        else:
            self.ckBoxMDOutput = False
            
           
    ### textBoxes ###        
    def txtBoxOffChanged(self, sender, args):     
        self.txtBoxOffOutput = sender.Text
                
        
    def txtBoxCoordChanged(self, sender, args):
        self.txtBoxCoordOutput = sender.Text 

    ### buttons ###
    def okButtonPressed(self, sender, args):
        numErrorProvider = System.Windows.Forms.ErrorProvider()
        try:
            int(self.txtBoxOffOutput)
            self.Close()    #trigger to close the GUI when button is pressed
            self.runNextOutput = True #if the ok button is pressed set runNextOutput as True
        except ValueError:        
            numErrorProvider.SetError(sender, 'Offset must be a number')
    def CnlButtonPressed(self, sender, args):
        self.Close()
        self.runNextOutput = False #if the cancel button is pressed set runNextOutput as False 

        
##########################-------------\-------------##########################

        ### Create a Class to define the second window ####

class followForm(Form):

    def __init__(self): #the __init__ method inside a class is its constructor
        self.Text = titleText             
        self.Icon = titleIcon        
        self.Size = winSize
        self.CenterToScreen()
        self.FormBorderStyle = FormBorderStyle.FixedDialog        
        self.runNextOutput = False  #how we decide if we want the next window to fire 
        uiWidth = self.DisplayRectangle.Width    #get the size of the form to use to scale form elements
        uiHeight = self.DisplayRectangle.Height
        self.Controls.Add(logo(uiWidth, uiHeight)) #add logo       
  
#############-------------\-------------#############   

        ### Buttons ## 

        #Create Ok Button
        btnOkClick = self.okButtonPressed #register click by user
        btnOkLoc = Point(uiWidth - ((btnWidth * 2) + spacing + 30), uiHeight - (btnHeight + spacing))
        btnOk = button('OK', btnOkLoc, btnOkClick) 

        #Create Cancel Button
        btnCnclClick = self.CnlButtonPressed #register click by user
        btnCnclLoc = Point(uiWidth - (btnHeight + spacing + 90), uiHeight - (btnHeight + spacing ))
        btnCancel = button('Cancel', btnCnclLoc, btnCnclClick)
        
        #add to Window
        self.Controls.Add(btnOk)
        self.Controls.Add(btnCancel)  

#############-------------\-------------#############

        ### Create Second Window Text ###
               
        userMessageScnd = Label()   #label displays texts
        font = fontMessage
        dimsionInputs()
        #message(ckBoxLWChecked, ckBoxAWChecked)
        userMessageScnd.Text = 'runNextOutput = ' + str(dimForm.runNextOutput) + '''\n\nThis is a contradiction! \n\nThis window only fired because runNextOutput was changed to True by the previous window! \n\nSo, sadly, it seems we can\'t link a series of windows driven by dynamic information without daisy-chaining python nodes together'''
        #userMessageScnd.Text = dimsionInputs().messText
        userMessageScnd.Font = font
        userMessageScnd.Location = Point(spacing, spacing)  #all location require a point object from system.Drawing to set the location.
        userMessageScnd.Size = Size(uiWidth-(spacing*2),(uiHeight))   #size the control with the width of the GUI to ensure it scales with different screen
        self.Controls.Add(userMessageScnd)       #this adds control element to the GUI

#############-------------\-------------#############

        ### Return Info From User ##
        
    ### buttons ###
    def okButtonPressed(self, sender, args):
        self.Close()    #trigger to close the GUI when button is pressed
        self.runNextOutput = True #if the ok button is pressed set runNextOutput as True#
    def CnlButtonPressed(self, sender, args):
        self.Close()

##########################-------------\-------------##########################

        ### And Run ##

dimForm = dimsionInputs()
followForm = followForm()    

Application.Run(dimForm)
  
if dimForm.runNextOutput:
    Application.Run(followForm)

OUT = dimForm.ckBoxESOutput, dimForm.ckBoxLWOutput, dimForm.ckBoxAWOutput, dimForm.ckBoxMDOutput, dimForm.txtBoxOffOutput, dimForm.txtBoxCoordOutput, 

i use Revit2024 (IronPython2 Package is loaded)


I use a icon8 .png how to deal with the file path ? or should i deal like “PyRevitToolbar”?

KR

Andreas

What specifically is the error you are getting?
i set this up a while ago for me so i can’t remember specifically what i needed to change to make it work but i’m sure that the .PNG shows in 2 locations was one of them

I am running this in R2023 with Cpython3 Engine.

image

2 Likes

Hello, Mr. Draxl take a look at Mr. Poupin’s posts, this should guide you in the right direction

Cordially
christian.stan

1 Like

@christian.stan ,

i tried both PyQt5 and 6 i was not able to install stuff. I had problems with Madlib and ifcopenshell too…
i did everything with pip

numpy and pandas works well.

KR

Andreas

sorry not yet competent enough on this UI aspect

cordially
christian.stan

1 Like

Hi,
using PyQt is possible with Dynamo however, as we are in a .Net environment, I rather recommend Winform or Wpf, just to take advantage of features like DataBinding and others

example with QtDesigner and Python

2 Likes

@c.poupin ,

Thank you

so that means i have to switch to c# ?

so my idea ist to write and perform the code in dynamo (py) and than in PyRevit(CostumToolbar)

when i use WPF or Winform… what should i use ?

My first step would be “select category” and than “filter by Name contains”

import clr

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import *

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

clr.AddReference('System')
from System.Collections.Generic import List

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

collector = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls)
all_walls = collector.WhereElementIsNotElementType().ToElements() #dropdown menu for categories

spec_typ = [i for i in all_schedules if i.Name.Contains("STB")] #Textbox

OUT = spec_typ

How is the workflow i “construct” the window ?

KR

Andreas

if the end use is pyRevit you might as well do the python code directly in pyRevit (unless there are dependencies with the Dynamo API)

Winform and Wpf remain possible with IronPython and PythonNet there are several examples on the forum (there is however an additional difficulty with PythonNet)

Then even if you code in Python learning C# is interesting (just to understand the multiple examples that exist on the Revit API)

1 Like

@c.poupin ,

i apsolutly agree with C#, i try also ZeroTouch… …but it is more complex regarding folder management and deep understanding how your computer is structured and dealing with costumized workflows…

@Draxl_Andreas

a example with Winform


import clr

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import *

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

clr.AddReference('System')
from System.Collections.Generic import List

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')
import System.Drawing
import System.Windows.Forms

from System.Drawing import *
from System.Windows.Forms import *

class Form27(Form):
	def __init__(self):
		collector = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls)
		self._all_walls = collector.WhereElementIsNotElementType().ToElements() 
		# out value
		self.selectWall = None
		self.userText = ""
		self.InitializeComponent()
	
	def InitializeComponent(self):
		self._buttonOK = System.Windows.Forms.Button()
		self._comboBox1 = System.Windows.Forms.ComboBox()
		self._label1 = System.Windows.Forms.Label()
		self._label2 = System.Windows.Forms.Label()
		self._textBox1 = System.Windows.Forms.TextBox()
		self.SuspendLayout()
		# 
		# buttonOK
		# 
		self._buttonOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right
		self._buttonOK.Location = System.Drawing.Point(241, 230)
		self._buttonOK.Name = "buttonOK"
		self._buttonOK.Size = System.Drawing.Size(88, 32)
		self._buttonOK.TabIndex = 0
		self._buttonOK.Text = "OK"
		self._buttonOK.UseVisualStyleBackColor = True
		self._buttonOK.Click += self.ButtonOKClick
		# 
		# comboBox1
		# 
		self._comboBox1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right
		self._comboBox1.FormattingEnabled = True
		self._comboBox1.Location = System.Drawing.Point(12, 74)
		self._comboBox1.Name = "comboBox1"
		self._comboBox1.Size = System.Drawing.Size(317, 21)
		self._comboBox1.Items.AddRange(System.Array[System.Object](self._all_walls))
		self._comboBox1.DisplayMember = "Name"
		self._comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList # force ready only
		self._comboBox1.TabIndex = 1
		self._comboBox1.SelectedIndexChanged += self.ComboBox1SelectedIndexChanged
		# 
		# label1
		# 
		self._label1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right
		self._label1.Location = System.Drawing.Point(13, 45)
		self._label1.Name = "label1"
		self._label1.Size = System.Drawing.Size(316, 23)
		self._label1.TabIndex = 2
		self._label1.Text = "label1"
		# 
		# label2
		# 
		self._label2.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right
		self._label2.Location = System.Drawing.Point(13, 111)
		self._label2.Name = "label2"
		self._label2.Size = System.Drawing.Size(316, 23)
		self._label2.TabIndex = 3
		self._label2.Text = "label2"
		# 
		# textBox1
		# 
		self._textBox1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right
		self._textBox1.Location = System.Drawing.Point(12, 138)
		self._textBox1.Name = "textBox1"
		self._textBox1.Size = System.Drawing.Size(317, 20)
		self._textBox1.TabIndex = 4
		# 
		# Form27
		# 
		self.ClientSize = System.Drawing.Size(341, 274)
		self.MinimumSize = self.ClientSize + System.Drawing.Size(20, 20)
		self.Controls.Add(self._textBox1)
		self.Controls.Add(self._label2)
		self.Controls.Add(self._label1)
		self.Controls.Add(self._comboBox1)
		self.Controls.Add(self._buttonOK)
		self.Name = "Form27"
		self.Text = "Form27"
		self.ResumeLayout(False)
		self.PerformLayout()

	def ComboBox1SelectedIndexChanged(self, sender, e):
		self.selectWall = sender.SelectedItem

	def ButtonOKClick(self, sender, e):
		self.userText = self._textBox1.Text
		self.Close()
  
form = Form27()
form.ShowDialog()
OUT = form.selectWall, form.userText
2 Likes

@c.poupin ,

would that be a good winform generator ?

can i use it also for python?

KR

Andreas

yes

1 Like