Posts by retug (25)

Project Management with Deltek

Deltek is a powerful tool to track all things AEC related. At my level, I use it mostly for project management and tracking project budgets. 

You can get a lot of powerful reports out of it quickly, but at face value, it's just a sea of data. We need to visualize this data to get a better feel for what's happening. The plot above shows hours on a fictitious project over time for differing employees.

Fun notes from fake project, brown engineer leaves company, olive main engineer goes back to school mid project, blue project manager scrambles to pick up the project mid way through.

If you use deltek, you can quickly get this data in an excel format from the project review tab and clicking "export to excel". 

The sample excel file will have the following format:

Person Date Hrs Checked
Help Proj Eng 1 10/14/2021 4 Checked
QAQC Eng   15  
QAQC Eng 9/14/2022 4 Checked
QAQC Eng 12/6/2022 2 Checked
QAQC Eng 5/8/2023 1 Unchecked
QA/QC Eng 5/9/2023 6 Unchecked
QA/QC Eng 5/10/2023 2 Unchecked
Help Eng 2   10  
Help Eng 2 11/21/2022 0.5 Checked
Help Eng 2 11/22/2022 5.5 Checked
Help Eng 2 11/23/2022 4 Checked
Drafter QAQC   4.5  
Drafter QAQC 4/27/2023 4.5 Checked
Drafter QAQC 5/15/2023 0 Unchecked
Proj Eng 3   130  
Proj Eng 3 9/15/2022 0.5 Checked

With this excel file, we can do a little python on the file, using pandas and matplotlib and create fun plots.

I am still quite a beginner with pandas and I am sure there is a much more elegant way to do the cumulative time vs date plot. I struggled through this, but the code works:


### Multiple Plots

df_all = pd.read_excel('H:\\MyGenericProject.xlsx', sheet_name = 'HoursForProject')
names = df_all.iloc[:,0]

my_column_changes = names.shift() != names        
index_change = [i for i, x in enumerate(my_column_changes) if x]

PersonHrs = {}
PersonDate = {}

for i in range(len(index_change)):
    if i == len(index_change)-1:
        PersonHrs[names[index_change[i]+1]] = df_all.iloc[index_change[i]+1:,2]
        PersonDate[names[index_change[i]+1]] = df_all.iloc[index_change[i]+1:,1]
    else: ###grabs the last person in the list
        PersonHrs[names[index_change[i]+1]] = df_all.iloc[index_change[i]+1:index_change[i+1],2]
        PersonDate[names[index_change[i]+1]] = df_all.iloc[index_change[i]+1:index_change[i+1],1]
sum_time = []
for i in PersonHrs.values():
    indiv_sum = []
    indiv_time = 0
    for time in i:
        indiv_time += time
for indiv_person_hrs, indiv_person_date in zip(sum_time, PersonDate.values()):
    DF = pd.DataFrame()
    DF['value'] = indiv_person_hrs
    person_date = pd.to_datetime(indiv_person_date) 
    DF = DF.set_index(person_date)


The code to do this and sample excel file is found on github.

In addition, I made a cool stacked bar plot that tracks hours on Engineering, Drafting and QA/QC type work.

This fake project worked out to be about 70% engineering, 23% drafting and 7% QA/QC.

Moving forward, I would like to access Deltek's API directly. It would be awesome to make these plots directly without having to export excel files and pull other data manually. If you have accessed Deltek's API before, let me know, it would be fun to explore how to do this.


Transfer Diaphragm Design

I have been working through some complicated diaphragm designs with a newer engineer recently and have been learning a lot about transfer diaphragms. I realized I was falling into some pitfalls and it was good working through this with the newer engineer. One of the great things about working with newer engineers is the fresh perspective and inquisitive nature.

The Sample Building

The building shown at the start of this blog post is a 20 story tall building, with the lower (2) floors being the typical transfer diaphragm type building, where the building core shifts a lot of its lateral load out to the perimeter concrete walls. See image below for large jump in shear at the transfer diaphragm in the building we inspect in this post.

I want to note that this building is completely made up, please take all numbers with a grain of salt and let’s focus on the relative values, not the values themselves. Please also let me know if you do diaphragm design differently, this is one person's perspective on diaphragm design.

A few notes on the building.

  • Total height 248ft
  • Typical story height is 12ft
  • 24” thick concrete core with moment frame outrigger system
  • 12” thick concrete slab for typical floor
  • 24” transfer diaphragms at levels 2 and 1
    • We will study a few differing transfer diaphragm setups
    • Level 2 Shown below

  • Building is subjected to an ELF type load
    • Base ETABs seismic seismic parameters
      • R = 8
      • Sds = 1.374
    • Total base shear is 1600 kip under ELF loading

My Previous Design process for Diaphragm Design

Let me outline the way I used to design diaphragms

  • Calculate the ELF applied story forces that a program like ETABs/RAM elements produces
    • For our sample building, which I have uploaded to github, we have the following applied diaphragm forces for an ELF type load case
    • Note at story 2, we have an applied force of 34.2kip








































































  • From here, I would then calculate the diaphragm design force according to ASCE 7.
    • For fun, let’s do a quick calc based on the seismic parameters input into the ETABs model. Controlled by Fpx_min and we are checking level (2)
    • Fpx = 0.2*Sds *Wp = 0.2*1.374*(5064kip) = 1392 kip
    • This works out to about 96.67psf over the 120’x120’ level 2 transfer diaphragm
  • Next, I would take the ratio of applied diaphragm force in the ELF case and compare that to the Fpx value, this would be the ratio I would use to amplify my section cut results by to do the design of my chords and collectors, you can maybe start to see the problem.
    • Amplification factor Fpx/Fx = (1392kip)/34.249kip =~ 40.5
  • A small aside, I built this tall 20 story building to demonstrate my point, but when working with smaller buildings that often do not have as explicit of transfer diaphragms, 6-8 stories, built into a hillside for example, the addition of lateral force resisting elements, these ratios are much smaller and seem like they might be reasonable.
  • With the amplification factor, in attempt to capture the minimum diaphragm design force, I amplify my shear force and moment diagram and design my design to resist these amplified forces. You can see that a factor of 40.5 is unreasonable and does not capture what the code is truly trying to capture with minimum diaphragm design forces and higher level mode effects. More on this later.

Sample buildings and iterations with level 2 Transfer Diaphragm

I have uploaded (3) differing ETABs models to github.

  1. The base model with a 24” thick transfer diaphragm

  1. The model with a 12” thick diaphragm and stiffness modifiers to account for cracking
  2. Same model as #1, but with a large hole in the transfer diaphragm



The new way of designing diaphragms:

Moving forward with diaphragm designs, I will not use this amplification method I outline above anymore. I think this method works for regular buildings, where there is little transfer of forces through diaphragms and for buildings where the lateral force resisting system is “regular”. My definition of regular is that shear force in the lateral force resisting system slowly accumulates on a story, by story basis. Backstay effects and transfer diaphragms are not "regular".

I will note that the sample building I have built, I would define as regular above level 2 and above, the shear force accumulates in the shaft as you move down the building until you hit level 2 and experience the large backstay effect.

The new way of designing diaphragms is running two overall design checks

  1. Running the overall ELF model, reviewing results in the diaphragm with the slicer
  2. Appling a diaphragm design force one level at a time and reviewing the results of the diaphragm with the slice

Please note that these forces need to be amplified for element being design, for example, a lot of collector forces need to be amplified by omega.

My understanding of diaphragm design forces is that they are to capture higher level mode effects where the max diaphragm design force at a specific level would not typically occur in the same assumed manner as the first mode. The first mode and corresponding ELF design forces is the correct way to design the lateral force resisting system and the design of transfer diaphragm forces. In a lot of cases for transfer diaphragms, the typical ELF load distribution will be the worst case diaphragm force.

Fun Sketches

You can see in the 1st mode assumed shape, the lateral load builds up and a majority of the lateral load will have to transferred at the transfer diaphragm to the stiff basement walls if the transfer diaphragm is stiff enough. In the case of the higher level mode deformed shape, the applied loads will typically cancel out as you move from story to story.

This approach is backed up by NEHPR Seismic Design Technical Brief #5, Seismic Design of Composite Steel Deck and Concrete Filled Diaphragms, a few excerpts below

Shear Results

­Onto the fun plots. All plots produced with results from the diaphragm slicer and matplotlib

Notes on the shear plots

  • These are clickable for closer inspection, I realize they are quite tiny. Click to review and zoom
  • You can see big differences in shear forces between the differing models
  • Switching from a 24” thick transfer diaphragm to a 12” diaphragm with stiffness reduction for cracking led to a large decrease in diaphragm shear
    • 1335 kip (12”) / 2380 kip (24” diaphragm) = 0.56
  • Note that this could lead to a negative feedback loop, I need more diaphragm strength, whoops, I have more diaphragm demand.
    • One thing to pay attention to
  • Note that the diaphragm Fpx graph (red line) is tiny in comparison to the other transfer diaphragm forces. This illustrates the pitfalls with my old design method. You live and learn and get better.
  • I was also surprised to see how little impact the large hole had on the shear diagram (black and yellow plot)

Moment Plot

Not much to note here, moment results are in line with what you would expect after reviewing the shear diagrams.

The Takeaway

If you have a transfer diaphragm, be sure to do two diaphragm designs, one based on the overall ELF response and one based on the diaphragm design force applied as individual load case. Even on buildings where diaphragms are not immediately obvious as transfer diaphragms, doing this procedure can reduce your transfer diaphragm forces significantly. Any change in building geometry or the addition of a new lateral force resisting line, consider doing a little extra work of two diaphragm designs and the savings could be well worth it.

Website Updates

I made a few updates to the site, mostly focusing on updating the home page sidebar to have some actual information. The old side bar was just place holder text from the tutorial I found, but the new side bar is populated with the last (3) comments made on the blog. 

Before I dig into the coding to make this happen, I want to make a small aside that my old desktop died that was used to make this site and I have moved onto a new laptop which I have been enjoying. I was bit freaked out when I lost the old desktop due to my poor IT backup practices. I deploy this site using git, but should move the site to github. I need to git this done, it looks like Heroku (the site I deploy to) can accommodate both git and github. 

The process to get the website back up and running on the new machine was pretty easy. Luckily, my SSD in the old desktop was not fried and I was able to copy all of the website files off of the SSD and xfer onto the new computer. I was able to copy all of my environment variables off of the heroku machine and paste them into the computer. After some struggling to get the local variables set up, I was able to begin working on the site again. 

Live Comment Feed

For awhile I wanted to pull a few of the recent comments on the site and have them populate the sidebar like most modern blog sites. Given the popularity of chat gpt, I figured I would give it a go to see if could maybe point me in the right direction. 

ChatGPT - Did it Work?

Not really.  Here is an example of how the interaction I had with chatgpt:

Me: write python to access django comments model and pull the last 3 comments from the database

Assuming you have set up your Django project and have a comments app with a Comment
 model, here's an example Python code to access the Comment model and retrieve the
 last three comments from the database:


from comments.models import Comment

last_three_comments = Comment.objects.order_by('-id')[:3]

for comment in last_three_comments:

In this code, we import the Comment model from the comments app, and use the objects
 manager to retrieve the last three comments from the database, sorted by the id
 field in descending order (so the most recent comments are first). We then loop
 over the comments and print their text to the console.

Note that the id field may not be the best way to sort comments, depending on how 
your app is set up. You may want to use a created_at or updated_at field instead.

The code that it spat out was correct, but I did not give it enough guidance to properly make this code usable for my specific project. The code produced by chatgpt will extract comments from the database, but this will not in a useable format for rendering into HTML. 

Given that I have not played with Django (package to make the website) in awhile, I forgot a lot of the basics. Once you have retrieved the data you want to be in the webpage, you need to pass this data to a view. This was the part that chatgpt missed and that I had forgotten. After some struggling, I was able to pass this data into my file.

class PostListView(ListView):
    model = Post
    template_name = 'blog/home.html'  # <app>/<model>_<viewtype>.html
    context_object_name = 'posts'
    ordering = ['-date_posted']
    paginate_by = 5
    def get_context_data(self, **kwargs):
        context = super(PostListView, self).get_context_data(**kwargs)
        context['last_three_comments']= Comment.objects.order_by('-date_added')[:3]
        #context['last_three_comments']= Post.objects.prefetch_related('comments').all()  
        return context

Setting up get_context_data enables me to pass both post and comment database information into the home.html file.

With a little tweaking to the html file:

{% for comment in last_three_comments %}
     <li class="list-group-item list-group-item-light">{{ }} on
 {{}} - <a class="article-title" href="{{}}">
{{ comment.body|safe|truncatewords:50}}</a></li>
{% endfor %}

the comments will render in and you get a new linked comment you can click on.

A nice update.

Updatable comments

Next item to tackle, allowing users to update their comments.

If you make a comment and you are the user that made the comment you should see an "Update" button next to your comment now.

The code to make this was pretty easy and I am starting to feel more comfortable working django.

Urls, Views and html:

path('commentupdate/<int:comment_pk>/', CommentUpdateView.as_view(), name='comment-update'),

class CommentUpdateView(LoginRequiredMixin, UpdateView):
    model = Comment
    pk_url_kwarg = "comment_pk"
    fields = ['body']
    success_url = '/'

{% if == user %}
     <a class = "btn btn-secondary btn-sm mt-1 mb-1" href = "/commentupdate/{{}}">Update</a>

In addition, I added a google tag to the concrete design tool on the site. For the longest time, I thought no one visited this portion of the site, which is probably true, but reviewing the analytics on the site revealed that actually no one had ever visited the site. I realized that I forgot to add a tag to that page. I hope to come back to the concrete design tool soon and document it, I think it is a powerful tool that someone might find useful

Diaphragm Slicer New and Improved

A long time coming... a professional level plugin for ETABs, or at least a whole lot better than the python diaphragm slicer.

User Input



This plugin will allow you to slice up your diaphragm in any direction you want, allowing you to see shear and moment along the length of the areas.

User Input

Start Point

This is the point at which you want to start the slicing. Only select an individual point, or the program will show you an error message


Select any number of objects, ensure all selected area objects share the same Z coordinate.

Input Vector

For slicing in the X direction, input X = 1, Y =0, ensure you input a positive integer. Input field will be limited to numbers and at this moment does allow for negative numbers.

Select Load Case

Pressing the Gather Cases button will retrieve all of the load cases in your current project, be sure to select one from the drop down box, this will be the outputted results from the section cut analysis

Number of Section

Input an integer between 2 and 1000, this will be the number of points that populate shear and moment diagrams

After all of these items are input, click on the run analysis button to view the results.

Reviewing the Results

3 Plots will generate after the program has run, shear (kips), moment (kip*ft), and a plot that shows how the program generated the cuts of your diaphragm for QA of your input.

The plots are all hoverable, allowing you to zoom and review results. In addition, a tabular data table is populated with location (ft), shear and moment being available to be copied and pasted in your favorite spreadsheet.

I have posted the code up on github. I hope to get this addin up on CSI's website in the future. If you have done this before, drop a comment below on how to do this. 

If you want to take this for test run, let me know below. I have yet to try this out in full production, I am sure there are bugs that need to be worked out.

The Coding

Now that the rough documentation is complete, let's dig into the fun stuff.

First thing that strikes me is that python is still freaking awesome! I was able to do this work in python in 233 lines of code and 1 file. This C# addin weighs in at around 1200 lines and 12 files. That's crazy in my opinion! Python is great for whipping up quick ideas.

Where is the variable explorer in Visual Studio Code?!?

Why in the world is there not a built in variable explorer in visual studio code similar to spyder for python. I never realized how great the variable explorer was until you were reviewing many many lines of nested list/array data. I found myself watching a lot of variables in visual studio code, super slow and tedious.

It looks like there might be a way to view large lists of data in visual studio code with a separate addin, but I did not dig into this in depth.


I ran into some trouble with the API when trying to make section cuts with the database tables methods. I had code that worked in ETABs v19, the OG diaphragm slicer, but the code python code would not work in v20 and the C# code I was trying to write would not work in v20 either. Turns out, the database tables headers between v20 and v19 were updated and no longer matched the visual column headers in the section cut tables. After some painful debugging we got this fixed. You can read more about my struggles here.

Matrices in C#

I use matrices in this program to convert back and forth from global coordinates to local coordinates and needed a matrix program if it was available. After some googling, it looked like mathnet.numerics was the most popular matrix package in C#, even above a c# clone of numpy it appears (package used in python to do matrix math). The mathnet package works, but is a bit clunky and I found the documentation hard to read and understand. Eventually, I was able to create a matrix and invert a matrix. Another odd one, it did not support matrix multiplication, I had to do that by hand!

Some of the weird syntax:

 public class GlobalCoordinateSystem
        public List<double> RefPnt { get; set; }
        public List<double> Vector { get; set; }
        public double hyp { get; set; }
        public double[,] R { get; set; }
        public string inverseMatrixText { get; set; }
        public Matrix<double> R_Matrix { get; set; }
        public double[,] R_Inv { get; set; }
        //This is the constructor, redefine the point?
        public GlobalCoordinateSystem(List<double> xyz, List<double> vector)
            RefPnt = xyz;
            hyp = Math.Sqrt((vector[0] * vector[0] + vector[1] * vector[1]));
            Vector = vector;
            R = new double[,] { { vector[0] / hyp, -vector[1] / hyp, 0 }, { vector[1] / hyp, vector[0] / hyp, 0 }, { 0, 0, 1 } };
            R_Matrix = Matrix<double>.Build.DenseOfArray(R);
            R_Inv = R_Matrix.Inverse().ToArray();

        public void glo_to_loc(GlobalCoordinateSystem globalCoords)
            double[] part1 = new double[] { X - globalCoords.RefPnt[0], Y - globalCoords.RefPnt[1], Z - globalCoords.RefPnt[2] };

            //the class will now have new attribute of local coordinates point.LocalCoords[0] = the X local coordinate system
            LocalCoords = new List<double>() { globalCoords.R_Inv[0, 0] * part1[0] + globalCoords.R_Inv[0, 1] * part1[1] + globalCoords.R_Inv[0, 2] * part1[2] ,
            globalCoords.R_Inv[1, 0] * part1[0] + globalCoords.R_Inv[1, 1] * part1[1] + globalCoords.R_Inv[1, 2] * part1[2],
            globalCoords.R_Inv[2, 0] * part1[0] + globalCoords.R_Inv[2, 1] * part1[1] + globalCoords.R_Inv[2, 2] * part1[2]};
        public void loc_to_glo(GlobalCoordinateSystem globalCoords)
            //this is the ref point
            double[] part1 = new double[] { X, Y, Z };

            //the class will now have new attribute of local coordinates point.LocalCoords[0] = the X local coordinate system
            GlobalCoords = new List<double>() { (globalCoords.R[0, 0] * X + globalCoords.R[0, 1] * Y + globalCoords.R[0, 2] * Z) + globalCoords.RefPnt[0],
            (globalCoords.R[1, 0] * X + globalCoords.R[1, 1] * Y + globalCoords.R[1, 2] * Z) + globalCoords.RefPnt[1],
            (globalCoords.R[2, 0] * X + globalCoords.R[2, 1] * Y + globalCoords.R[2, 2] * Z) + 0}; //unsure why this the way to do this. review in the future. should be the line below, without the 0
            //(globalCoords.R[2, 0] * X + globalCoords.R[2, 1] * Y + globalCoords.R[2, 2] * Z) + globalCoords.RefPnt[2]};

Another point, I need to review why my Z coordinate kept messing up in this process.

C# Plotting

I settled on a plotting package called live charts in C#. This package seemed to be the go-to package back in 2018, but it seems like maybe it has stopped being supported by its developers. Again, documentation was kinda hard to find and when it was able to found, it was hard to understand. More struggling and pain to get to the pretty graphs. I do like the graphs are live, zoomable and interactive. The out-of-the-box microsoft graphs had much less of the interactiveability built into them.

UI Development

I went through a few differing versions of UIs and finally called it good enough where it was. My favorite color is orange, so I wanted to through some orange in the plots, hopefully the plots are not too painful on the eyes.

I also set up a linear gradient brush on the background because why not, it seems like most modern UI's are using color gradients.

A few snips of UI in development

A bit too generic, looks like every other windows form

A bit too much orange

Just right, enough flair to know this guy knows what he's doing, but not too visually distracting:

Eventually I want to get back into this and make rounded buttons and pretty up the tabular data, but I wanted to be done with this project and called it quits at good enough.

Coding for the UI was actually pretty enjoyable in C#, there are a lot of good videos out there on how to make your form look good.

This bit of code makes the linear gradient in the background:

private void Form1_Paint(object send, PaintEventArgs e)
            Graphics mgraphics = e.Graphics;
            Pen pen = new Pen(Color.FromArgb(255, 140, 105), 1);
            Rectangle area = new Rectangle(0, 0, this.Width - 1, this.Height - 1);

            System.Drawing.Drawing2D.LinearGradientBrush lGB2 = new System.Drawing.Drawing2D.LinearGradientBrush(area, Color.FromArgb(255, 255, 255), Color.FromArgb(159, 159, 159), LinearGradientMode.Vertical);
            mgraphics.FillRectangle(lGB2, area);
            mgraphics.DrawRectangle(pen, area);

Please take this for a spin and let me know if you have any feedback!

EDIT 04/19/2023:

The section cut tool is only correct slices in the left right direction! (loading in the X global direction typically) I discovered this after using this on a real project. 

From ETABs documentation:

How is the plane of the section cut defined when drawn in a 3D view?
Expanded Question: When a section cut is drawn in a 3D view, only the X and Y coordinates are provided. How is the section-cut plane defined?

Answer: When using the Draw > Draw Section Cut command, forces are reported in the section-cut coordinate system which is defined by three axes (1,2,Z) as follows:

Section-cut 1 axis is located within the plane parallel to the global X-Y plane, and rotates counterclockwise from the global X axis according to the user-defined parameter Angle (X to 1).
Section-cut 2 axis is also located within the plane parallel to the global X-Y plane, though it is oriented 90° counterclockwise from the section-cut 1 axis.
Section-cut Z axis is parallel to the global Z axis.

I will need to update the code to pull right combination of either F1 or F2. This should not be too difficult to do.

ETABs Plugin - Part 2

Having nailed down the initial steps of creating a plugin, I set out to do some real functional work with the plugin. Being naive, I went off the deep end to begin programming the diaphragm slicer, but was quickly met by a sea of syntax and foreign looking code that was difficult to navigate. I had to go back to what all good programmers do, watching youtube videos.

The Code

The example plugin above will gather all of the load combinations in your ETABs project, populate them in a "datagrid view" and combobox and will report the joint reaction forces at a specific node under the selected load combination. I followed this youtube video, thank you Structure Detailing, the video was very informative and helpful!

The code to make this plugin has been upload on github.

New Syntax

From the video, the syntax of {get; set;} in a C# class appeared multiple times.

public class JointReaction
        public string Name { get; set; }
        public string LoadCase { get; set; }
        public double F1 { get; set; }
        public double F2 { get; set; }
        public double F3 { get; set; }
        public double M1 { get; set; }
        public double M2 { get; set; }
        public double M3 { get; set; }

This notation appears related to "encapsulation" in C# and allows this class to be both private (hidden from other classes and other parts of the program) and public. The JointReaction class is then instantiated in the main Form1.cs code.

           React.Name = Name;
           JReact.LoadCase = LoadCase[0];
           JReact.F1 = F1[0];
           JReact.F2 = F2[0];
           JReact.F3 = F3[0];
           JReact.M1 = M1[0];
           JReact.M2 = M2[0];
           JReact.M3 = M3[0];

Calling the .F1 attribute on the class JReact somehow triggers the {get; set;} notation. I have to dig into more C# to fully understand this {get; set;} notation.

ETABs API - Unclear Returns

API calls to ETABs are foreign to me, how can there be no explicit return?

Take for example the following code:

string Name = "";
            eItemTypeElm ItemTypeElm;
            int NumberResults = 1;
            string[] Obj = null;
            string[] Elm = null;
            string[] LoadCase = null;
            string[] StepType = null;
            double[] StepNum = null;
            double[] F1 = null;
            double[] F2 = null;
            double[] F3 = null;
            double[] M1 = null;
            double[] M2 = null;
            double[] M3 = null;
            int x = -1;
            //Method below acts on a string

            //unsure why eItemTypeElm.Element does not need to be initiated
            //anything that is ref is returned, not input
            x = _SapModel.Results.JointReact("4", eItemTypeElm.Element, ref NumberResults, ref Obj, ref Elm, ref LoadCase, ref StepType, ref StepNum, ref F1, ref F2, ref F3, ref M1, ref M2, ref M3);

The confusing part of this code is that the output of _SapModel.Results.Joint(...), x, is merely an integer, 1. I have no idea how this method writes/returns the values of the results to F1, F2, F3... Anytime I have written methods, I have direct return statement and it would typically be written directly to the output variable, x. If you know what the syntax might look like in C# to make this complicated output, please let me know, I would be interested in learning how to return multiple data types, lists, and values.

FYI, the code above will output the joint reaction forces for the unique node "4" in the ETABs model. These are the values that are populated in the initial video.

DataGridViews - A Ray of Hope for Programming

Data grid views have to be the best thing I have discovered in C#. Coming from python and the miserable experience that was PyQt5 to make a GUI, this is a miracle. The code to make tables is very clean and concise. If I never have to write PyQt5 code again, I will be happy. Side note, I have been meaning to make a post on PyQt5 GUIs with ETABs, another post for a later time. A sample of the mess that is PyQt5:

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data
        self._headers = ['Frame Size', 'Cnx Type', 'Demand (kip)', 'Capacity (kip)', 'ETABs Frame #']

   def data(self, index, role):
        if role == QtCore.Qt.DisplayRole:
            # See below for the nested-list data structure.
            # .row() indexes into the outer list,
            # .column() indexes into the sub-list
            return self._data[index.row()][index.column()]
        if self._data[index.row()][index.column()] == 'SS/DS cnx NG' and role == QtCore.Qt.BackgroundRole :
            return QBrush(
        if self._data[index.row()][index.column()] == 'No Values' and role == QtCore.Qt.BackgroundRole :
            return QtGui.QColor('#FFA500')
        if self._data[index.row()][index.column()] == 'DS cnx Ok' and role == QtCore.Qt.BackgroundRole :
            return QBrush(Qt.yellow)
    def rowCount(self, index):
        # The length of the outer list.
        return len(self._data)

Now that I have learned the basics of the plugin and the ETABs API, I think I should be able to struggle through my first bit of custom coding.

1 2 3 Next Last


Site Info

  • Latest Comments