Python: Create a ZIP file and add files to it

With the following code, we can easily create a ZIP file in Python, and then add objects to it.

from zipfile import ZipFile

# Here, I am defining the zip file and path from a project called u.name
# The 'w' means 'write'
# Don't forget the .zip in the file name
zipName = u.name + "_" + datetime.today().strftime("%Y%m%d%H%M") + ".zip"
zipPath = settings.MEDIA_ROOT / u.name
zipObj = ZipFile(zipPath / zipName,'w')

# We can then add a file to our ZIP. This file already exists elsewhere, under the savepath variable. 
# filename is the name of this file as saved within the ZIP file
zipObj.write(savepath, filename)

#finally, remember to close the ZIP file once done
zipObj.close()

C#: Convert an Image to a BitmapImage

How to convert a System.Drawing.Image to a System.Windows.Media.Imaging.BitmapImage.

using System.Windows.Media.Imaging;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

//...

public BitmapImage Convert(Image img)
{
    using (var memory = new MemoryStream())
    {
        img.Save(memory, ImageFormat.Png);
        memory.Position = 0;

        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = memory;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();

        return bitmapImage;
    }
}

This and other approaches to solving this problem can be found on this StackOverflow page.

How to implement IEnumerable in C#

When you create your own class, you may want an easy way to cycle though the data within the class.

Ever wondered how the ‘foreach’ works?

foreach (double val in MyClass)
{
    //do something with val
}

Something like this is only possible if we tell MyClass what values within MyClass we want to cycle through.

This is possible by implementing IEnumerable.

An example

Let’s say MyClass looks like this:

using System.Collections.Generic;

namespace MyNameSpace
{
    public class MyClass
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;
    }
}

What if we create an instance of MyClass, and want to run a foreach loop on it?

What we’d like is for the foreach to loop through the three values contained in myData.

But it doesn’t work yet. What’s missing?

error enumerator

The error reads: foreach statement cannot operate on variables of type ‘MyNamespace.MyClass’ because ‘MyNamespace.MyClass’ does not contain a public definition for ‘GetEnumerator’.

So how do we add the definition for GetEnumerator?

How to implement IEnumerable in C#

We do this by implementing the IEnumerator interface to our class.

using System.Collections.Generic;
using System.Collections;

namespace MyNamespace
{
    public class MyClass : IEnumerable
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;

        public IEnumerator GetEnumerator()
        {
            foreach(double val in myData)
            {
                yield return val;
            }
        }
    }
}

We have done three things:

  1. Added using System.Collections;
  2. Added : IEnumerable to let the compiler know that there’s something in the class we can iterate through
  3. Added method GetEnumerator(). This is what is called when we try to iterate through an instance of the class.

Note the use of the special keyword yield. This allows us to return an enumerated value without breaking from the GetEnumerator method.

So, how does this look now?

working-enumerator

Perfect!

Specify a return type with IEnumerable

Notice how we never specified anywhere that the enumerator would be returning a double?

By default, the enumerator returns an object. When we run our foreach loop, it is clever enough to implicitly cast each enumerated output as a double, since we specified that we wanted each item to be of type double.

What if another programmer, using our class, doesn’t know to expect a double, and they just use a var (or something else)?

We get an object instead – and an error.

enum-error-var

Can we tell GetEnumerator which data type it should be returning?

In your class, implement IEnumerable<T> instead. Then, there are two methods (not one) to implement.

using System.Collections.Generic;
using System.Collections;

namespace MyNamespace
{
    public class MyClass : IEnumerable<double> //Specify your return type in the angle brackets.
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;

        //Put your enumerator code in this method. Specify your return type in the angle brackets again here.
        public IEnumerator<double> GetEnumerator()
        {
            foreach(double val in myData)
            {
                yield return val;
            }
        }

        //This method is also needed, but usually you don't need to change it from this.
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

Now, we should find that, even if we type ‘var’, Visual Studio knows what type to expect.

var-enum-working

This is the way that I would recommend. Specify your data types – it will help reduce bugs and mistakes later on in your project.

C#: Get DPI of screen

It used to be the case that most monitors had a screen resolution of 96DPI. When developing software, this meant developers could hard-code values when designing their interfaces.

This is no longer the case. The latest generation of screens and laptops are likely to have very high resolutions, perhaps greater than 200DPI (dots per inch).

At this resolution, fonts and windows would become uselessly small. This is my 13″ laptop display at 3200×1800:

desktop

As you can see, it is quite unusable. Windows can therefore scale up window elements to a more natural size.

Here is my screen at 200% scaling:

desktop2

What this means for developers

When we develop software, we must make sure it works well across a range of user environments, and this now includes testing for a range of screen resolutions.

Fortunately, modern UI libraries will automatically scale built-in elements according to the screen. For example, if you specify the width of a button to be 100 pixels in WPF, on my screen with a scaling of 200%, this button would automatically be rendered at 200 pixels.

However, you may find that some custom elements do not scale properly. You may also be using some ‘fudge factors’ in your code-behind (such as the C# file associated with a XAML file in WPF) that are constant. These may be written in such a way that your UI library doesn’t scale them.

For example, say you have a margin of 10 pixels written in your code-behind. How do you tell your program to scale up this value to any scaling factor?

Get the scaling factor in C#

This is quite easy. Simply calculate the following value in your code:

double factor = System.Windows.PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice.M11;

Then, wherever you have a constant value in your code-behind, multiply it by factor, e.g.

button.Left = 10 * factor;

Output

The code above returns a value that is usually between 1 and 2.5. If the user’s system DPI settings are set to 200%, a value of 2.0 should be returned.

To calculate the scaling factor in terms of DPI, multiply by 96. For example, if the system settings are at 200%, the DPI is 196DPI. Note that this value can be changed by the user if they want the screen to appear smaller or larger, so the concept of an ‘inch’ doesn’t always rigorously apply.

C#: Simple BackgroundWorker example

Here is a simple example on how to use the BackgroundWorker class in C#.

What is the BackgroundWorker?

Are you writing a Windows program with some heavy calculations going on in the background? The BackgroundWorker class will take those calculations and put them in a separate thread, helping to prevent your UI from freezing up.

Multi-threaded programming is tricky at the best of times. The fundamental problem is that you don’t want two threads trying to access the same bit of memory at once. The BackgroundWorker simplifies a lot of the work you would otherwise need to do yourself, but it can still be difficult to set it up.

BackgroundWorker example

This example downloads an image from the Internet and saves it to the user’s desktop. It does this 10 times (so we can see how to monitor progress). The image address is provided by the user in a WPF form, and we report the number of files downloaded in a progress bar on that form.

The form stays responsive the whole time, and doesn’t lock up while the files are being downloaded – something which would happen if we didn’t create our own thread.

background worker c# example program UI

Example code

Create a new C# WPF project in Visual Studio.

XAML

We need to create a form with a textbox, a button, a progress bar and a label. This isn’t essential to the BackgroundWorker, but it will help create our example program.

If you aren’t familiar with WPF or XAML, XAML is the markup language used to create interfaces in WPF, much like HTML to web development. Here is the contents of my MainWindow.XAML used to create the form above.

<Window x:Class="BackgroundWorkerExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BackgroundWorkerExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    
    <DockPanel>
        
        <DockPanel LastChildFill="True" DockPanel.Dock="Top" Margin="10">
            <Label DockPanel.Dock="Left" Width="100">URL</Label>
            <Button Name="btnGo" Click="btnGo_Click" DockPanel.Dock="Right" Width="100">Go</Button>
            <TextBox Name="tbURL"/>
        </DockPanel>
        
        <DockPanel LastChildFill="True" DockPanel.Dock="Top" Margin="10">
            <Label DockPanel.Dock="Left" Width="100">Progress</Label>
            <ProgressBar DockPanel.Dock="Bottom" Name="progBar" Value="0" />
        </DockPanel>

        <DockPanel LastChildFill="True">
            <Label DockPanel.Dock="Top">Status</Label>
            <ScrollViewer><Label Name="lblStatus" /></ScrollViewer>
        </DockPanel>
        
    </DockPanel>
    
</Window>

C# code

The actual code goes into MainApplication.xaml.cs. Here is the contents of my file.

It looks pretty long, but it’s not so bad. A lot of it is actually comments…

using System;
using System.Windows;
using System.ComponentModel;
using System.IO;

namespace BackgroundWorkerExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnGo_Click(object sender, RoutedEventArgs e)
        {
            lblStatus.Content = "";

            BackgroundWorker worker = new BackgroundWorker();

            //BackgroundWorker is event-driven. We use events to control what happens
            //during and after calculations.
            //First, we need to set up the different events.
            worker.DoWork += Worker_DoWork;
            worker.ProgressChanged += Worker_ProgressChanged;
            worker.WorkerReportsProgress = true;
            worker.RunWorkerCompleted += Worker_RunWorkerCompleted;

            //Then, we set the Worker off. 
            //This triggers the DoWork event.
            //Notice the word Async - it means that Worker gets its own thread,
            //and the main thread will carry on with its own calculations separately.
            //We can pass any data that the worker needs as a parameter.
            worker.RunWorkerAsync(tbURL.Text);
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            //DoWork is the most important event. It is where the actual calculations are done.

            System.Net.WebClient client = new System.Net.WebClient();

            for (int i = 0; i < 10; i++)
            {
                //We pass data to the worker using the Argument property.
                //Don't try to read data from the form directly.
                string url = (string)e.Argument;

                //download image
                byte[] imageStream = client.DownloadData(url);
                MemoryStream memoryStream = new MemoryStream(imageStream);
                System.Drawing.Image img = System.Drawing.Image.FromStream(memoryStream);

                //save image to computer
                string desktop = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
                img.Save(desktop + @"\image " + i.ToString() + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

                //Now that the image is saved, we can update the Worker's progress.
                //We do this by going back to the Worker with a cast
                int progress = (i + 1) * 10; //Between 0-100
                ((BackgroundWorker)sender).ReportProgress(progress);
            }

            //When finished, the thread will close itself. We don't need to close or stop the thread ourselves.  
        }

        private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //This method is called whenever we call ReportProgress()
            //Note that progress is not calculated automatically. 
            //We need to calculate the progress ourselves inside Worker_DoWork.
            //This method is optional.

            lblStatus.Content += e.ProgressPercentage.ToString() + "% complete. \n";
            progBar.Value = e.ProgressPercentage;
        }

        private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //This method is optional but very useful. 
            //It is called once Worker_DoWork has finished.

            lblStatus.Content += "All images downloaded successfully.";
            progBar.Value = 0;
        }


    }
}

Running the program

Here is a picture of a satsuma.

satsuma-mandarin[1]

Here is its URL:

http://james-ramsden.com/wp-content/uploads/2016/05/satsuma-mandarin1.jpg

If we put this URL into this program, we should be able to download this image 10 times over onto our desktop.

backgroundworker c# example program running

Here it is downloading the images, depositing lots of satsumas on my desktop. Or are they mandarins? I don’t really know.

mandarins satsumas

You may also want to try downloading some larger images that take longer to download. The BackgroundWorker works as expected. The interface is updated with each successful download. (If we didn’t use the BackgroundWorker, the interface would freeze until all 10 had been downloaded.) And the interface remains responsive the entire time.

Further reading

There are many ways to multithread in .NET, and the correct choice depends on what you are trying to do.

For instance, I recently had heavy calculations on a problem in Grasshopper, on entirely back-end code. This was dealt with by using Parallel.ForEach loops, which converts a ForEach loop into a multi-threaded equivalent.

If you’re looking to calculate something as quickly as possible using all the cores on your machine, then this is a good place to start. This approach depends on your task being able to be divided up into independent subtasks, so that each subtask can be calculated separately. (If one subtask depends on the output of another, it is going to be difficult to multithread.)

All in all, multithreading is a big topic, with many approaches possible, and there are many ways you can unfortunately get it wrong. It’s not something you are going to master in a day. But it’s worth spending the time on it, and it’s very rewarding when you do get it working 🙂

Git: Create a new branch and push to Bitbucket

For a team project, we are using Git and Bitbucket to manage our repository.

The setup

Somebody else has started a new project in a new repository. This repository can be seen on BitBucket, with a single master branch.

I want to download this repository to my own computer. I want to work on the project in my own branch, periodically saving the branch back to the repository for safety. When I’m finished, I want to merge my branch back with the master branch.

How do I do this?

Here is my cheatsheet for creating a new branch from the master branch on an existing project, working on it, then pushing my branch back to the repository.

Git cheatsheet

In Windows Explorer, create or open a folder to hold your various Git projects. In it, right-click and Git Bash Here.

Clone from the server to your computer:

git clone https://bitbucket.org/your-teamname/project-name

This will create a new folder on your computer with the repository files inside.

Change directory to go inside the newly created project folder:

cd project-name

Create a branch:

git branch JR-branch

Use checkout to switch to a branch:

git checkout JR-branch

Add any new files:

git add * 

Commit any changes. Note that nothing is uploaded yet!

git commit -m "comment"

Upload (“push“) your changes to the repository:

git push origin JR-branch

You should now see your branch on your Bitbucket project.

References

The ‘no deep shit’ guide to Git – highly recommended

C#: Recursively get all files in a folder and its subfolders

How to recursively get all files in a folder, including files contained within subfolders.

Or, in other words, how to find every file contained within a parent folder, and within any folders inside the parent folder.

The easy way

There is a method built into .NET, though it is buried as an overload, so you may not have known it was there until someone points it out to you…

foreach (string file in System.IO.Directory.GetFiles(
    parentDirectory, "*",SearchOption.AllDirectories))
{
    //do something with file
}

This loops through every file contained within the folder, including all files contained within any subfolders. Each loop returns a string of the address of each file.

The second parameter is a search filter. The value above of "*" simply means “return anything”. We could filter for Word documents by changing this to "*.docx", for example.

The alternative way

Microsoft offered a solution of their own in this old article, which provides an interesting iterative approach.

I have modified the above method slightly to also include files immediately within the parent directory. Microsoft’s solution currently doesn’t do this

The code below adds all filepaths to a List. If you wish to do something else, you need to change what happens within DoAction(). You might actually want to give DoAction a more sensible name too, depending on what your action is.

        List<string> files = new List<string>();

        private void getFilesRecursive(string sDir)
        {
            try
            {
                foreach (string d in Directory.GetDirectories(sDir))
                {
                    getFilesRecursive(d);
                }
                foreach (var file in Directory.GetFiles(sDir))
                {
                    //This is where you would manipulate each file found, e.g.:
                    DoAction(file);
                }
            }
            catch (System.Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        private void DoAction(string filepath)
        {
            files.Add(filepath);
        }

Get file types supported by BitmapImage

The BitmapImage class is the preferred way of handing image files in a WPF application.

A great advantage of BitmapImage is that it can open and handle a wide range of image types. It uses the Windows Imaging Component, which allows third parties to incorporate custom image formats into BitmapImage.

Ultimately, what this means is that, quite amazingly, I am able to open fancy file types like Nikon’s NEF raw format in a way that feels native in my C# applications.

But this presents a challenge. If a user wants to open an image in my application, how do I know if BitmapImage can handle it?

I could use a try-catch, but this is quite inefficient. And since BitmapImage is extensible, I can’t just check the file extension against a hard-coded list. I need to be able to check what file types are supported on a particular machine.

I presented this problem on this Stack Overflow post, where NineBerry provided a solution.

How the solution works

The gist of NineBerry’s solution is that we look in the registry for any third party codecs that help BitmapImage to open any additional file types. Once we have found them, we can then read which file extensions are typically associated with these file types.

NineBerry’s solution doesn’t find natively supported file types. We can use Microsoft’s documentation to hard-code these natively-supported types.

Then, when a user wants to open a file, we can compare its file extension with the extensions we found above.

Note that this isn’t fool-proof. A file with a supposedly valid extension may still not open. The file may be corrupted, have an incorrect extension, or the extension may have multiple uses. Still, this method should catch the vast majority of unsuitable files, and help to speed up your program.

How to use the class in your program

I took NineBerry’s code and turned it into a class with a few useful methods and properties. Here you can…

Check if a filename contains a BitmapImage-suitable file extension

string path = "myImage.jpg";
BitmapImageCheck bic = new BitmapImageCheck();
bool imageValid = bic.IsExtensionSupported(path); //returns true

path can be a file name with extension, an extension by itself, or a full address.

Get a list of supported file extensions

Get a list of file extensions provided natively, provided by third party decoders, or these two lists combined.

BitmapImageCheck bic = new BitmapImageCheck();
string[] supportedExtensions = bic.AllSupportedExtensions;

Get information about the decoders found on your system

Use the ToString() method to get information about where support for different file extensions is coming from.

BitmapImageCheck bic = new BitmapImageCheck();
MessageBox.Show(bic.ToString());

bitmapimage image extensions messagebox

C# class to get supported file types

Copy the code below into a new C# file in your project. The only thing you might want to change is the namespace.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32;

namespace WpfApplication1.Classes
{
    /// <summary>
    /// Provides methods for checking whether a file can likely be opened as a BitmapImage, based upon its file extension
    /// </summary>
    public class BitmapImageCheck : IDisposable
    {
        #region class variables
        string baseKeyPath;
        RegistryKey baseKey;
        private const string WICDecoderCategory = "{7ED96837-96F0-4812-B211-F13C24117ED3}";
        private string[] allExtensions;
        private string[] nativeExtensions;
        private string[] customExtensions;
        #endregion

        #region constructors
        public BitmapImageCheck()
        {
            if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
            {
                baseKeyPath = "Wow6432Node\\CLSID";
            }
            else
            {
                baseKeyPath = "CLSID";
            }
            baseKey = Registry.ClassesRoot.OpenSubKey(baseKeyPath, false);
            recalculateExtensions();
        }
        #endregion

        #region properties
        /// <summary>
        /// File extensions that are supported by decoders found elsewhere on the system
        /// </summary>
        public string[] CustomSupportedExtensions
        {
            get
            {
                return customExtensions;
            }
        }

        /// <summary>
        /// File extensions that are supported natively by .NET
        /// </summary>
        public string[] NativeSupportedExtensions
        {
            get
            {
                return nativeExtensions;
            }
        }

        /// <summary>
        /// File extensions that are supported both natively by NET, and by decoders found elsewhere on the system
        /// </summary>
        public string[] AllSupportedExtensions
        {
            get
            {
                return allExtensions;
            }
        }
        #endregion

        #region public methods
        /// <summary>
        /// Check whether a file is likely to be supported by BitmapImage based upon its extension
        /// </summary>
        /// <param name="extension">File extension (with or without leading full stop), file name or file path</param>
        /// <returns>True if extension appears to contain a supported file extension, false if no suitable extension was found</returns>
        public bool IsExtensionSupported(string extension)
        {
            //prepare extension, should a full path be given
            if (extension.Contains("."))
            {
                extension = extension.Substring(extension.LastIndexOf('.') + 1);
            }
            extension = extension.ToUpper();
            extension = extension.Insert(0, ".");

            if (AllSupportedExtensions.Contains(extension)) return true;
            return false;
        }
        #endregion

        #region private methods
        /// <summary>
        /// Re-calculate which extensions are available on this system. It's unlikely this ever needs to be called outside of the constructor.
        /// </summary>
        private void recalculateExtensions()
        {
            customExtensions = GetSupportedExtensions().ToArray();
            nativeExtensions = new string[] { ".BMP", ".GIF", ".ICO", ".JPEG", ".PNG", ".TIFF", ".DDS", ".JPG", ".JXR", ".HDP", ".WDP" };

            string[] cse = customExtensions;
            string[] nse = nativeExtensions;
            string[] ase = new string[cse.Length + nse.Length];
            Array.Copy(nse, ase, nse.Length);
            Array.Copy(cse, 0, ase, nse.Length, cse.Length);
            allExtensions = ase;
        }

        /// <summary>
        /// Represents information about a WIC decoder
        /// </summary>
        private struct DecoderInfo
        {
            public string FriendlyName;
            public string FileExtensions;
        }

        /// <summary>
        /// Gets a list of additionally registered WIC decoders
        /// </summary>
        /// <returns></returns>
        private IEnumerable<DecoderInfo> GetAdditionalDecoders()
        {
            var result = new List<DecoderInfo>();

            foreach (var codecKey in GetCodecKeys())
            {
                DecoderInfo decoderInfo = new DecoderInfo();
                decoderInfo.FriendlyName = Convert.ToString(codecKey.GetValue("FriendlyName", ""));
                decoderInfo.FileExtensions = Convert.ToString(codecKey.GetValue("FileExtensions", ""));
                result.Add(decoderInfo);
            }
            return result;
        }

        private List<string> GetSupportedExtensions()
        {
            var decoders = GetAdditionalDecoders();
            List<string> rtnlist = new List<string>();

            foreach (var decoder in decoders)
            {
                string[] extensions = decoder.FileExtensions.Split(',');
                foreach (var extension in extensions) rtnlist.Add(extension);
            }
            return rtnlist;
        }

        private IEnumerable<RegistryKey> GetCodecKeys()
        {
            var result = new List<RegistryKey>();

            if (baseKey != null)
            {
                var categoryKey = baseKey.OpenSubKey(WICDecoderCategory + "\\instance", false);
                if (categoryKey != null)
                {
                    // Read the guids of the registered decoders
                    var codecGuids = categoryKey.GetSubKeyNames();

                    foreach (var codecGuid in GetCodecGuids())
                    {
                        // Read the properties of the single registered decoder
                        var codecKey = baseKey.OpenSubKey(codecGuid);
                        if (codecKey != null)
                        {
                            result.Add(codecKey);
                        }
                    }
                }
            }

            return result;
        }

        private string[] GetCodecGuids()
        {
            if (baseKey != null)
            {
                var categoryKey = baseKey.OpenSubKey(WICDecoderCategory + "\\instance", false);
                if (categoryKey != null)
                {
                    // Read the guids of the registered decoders
                    return categoryKey.GetSubKeyNames();
                }
            }
            return null;
        }

        #endregion


        #region overrides and whatnot

        public override string ToString()
        {
            string rtnstring = "";

            rtnstring += "\nNative support for the following extensions is available: ";
            foreach (var item in nativeExtensions)
            {
                rtnstring += item + ",";
            }
            if (nativeExtensions.Count() > 0) rtnstring = rtnstring.Remove(rtnstring.Length - 1);

            var decoders = GetAdditionalDecoders();
            if (decoders.Count() == 0) rtnstring += "\n\nNo custom decoders found.";
            else
            {
                rtnstring += "\n\nThese custom decoders were also found:";
                foreach (var decoder in decoders)
                {
                    rtnstring += "\n" + decoder.FriendlyName + ", supporting extensions " + decoder.FileExtensions;
                }
            }

            return rtnstring;
        }

        public void Dispose()
        {
            baseKey.Dispose();
        }
        #endregion
    }
}

Get more information about the decoders on your system

The public methods implemented were intended to make available a basic level of file extension information.

For more advanced tasks, some of the private methods in the class could be made public. It is possible to expose further information about each codec, such as their locations in the registry.

C#: Convert all images in a folder from PNG to JPG

How to convert all images in a folder from PNG to JPG.

This method is written in C#. Input a folder path. The code will then find all files with .png file extensions in that folder, and save them again as .jpg files. Original files will not be deleted.

The Image.Save method allows for a wide range of image formats including bmp, tiff and gif. You can edit the code below for your own file formats.

C# method to convert images to JPG

        /// <summary>
        /// Converts all images in a folder to JPG
        /// </summary>
        /// <param name="folder">String representing folder location</param>
        public void ToJPG(string folder)
        {

            foreach (string file in System.IO.Directory.GetFiles(folder))
            {
                string extension = System.IO.Path.GetExtension(file);
                if (extension == ".png")
                {
                    string name = System.IO.Path.GetFileNameWithoutExtension(file);
                    string path = System.IO.Path.GetDirectoryName(file);
                    Image png = Image.FromFile(file);
                    png.Save(path + @"/" + name + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
                    png.Dispose();
                }
            }
        }

DDY Design Day weather file reader in Grasshopper C#

How to open a DDY (Design Day) file in Grasshopper.

Design day files are used for performing energy calculations in building design during represenative climatic extremes in summer and winter. Importing weather files into Grasshopper can be useful for energy analyses in Ladybug/Honeybee.

Download

Download the Grasshopper file here, which contains a C# component which you can run or edit. The code itself is pasted below, which could be used for any C# application.

You will also need your own DDY weather files. You can download them from here. Find the file you are interested in. Once you have downloaded the ZIP file, there should be a DDY file inside it.

C# code

  private void RunScript(string path, int y, ref object A)
  {

    int counter = 0;
    string ln;
    List<string> x = new List<string>();

    // Read the file
    System.IO.StreamReader f = new System.IO.StreamReader(path);
    while((ln = f.ReadLine()) != null)
    {
      x.Add(ln);
      counter++;
    }

    f.Close();



    var rtntree = new DataTree<String>();

    //strip comments
    for(int i = 0; i < x.Count; i++)
    {
      int ind = x[i].IndexOf('!');
      try{x[i] = x[i].Substring(0, ind);}catch{}
    }

    //reconstruct as one line
    string wholefile = "";
    foreach (string line in x) wholefile += line;

    //split into sections and fields
    List<List<string>> file = new List<List<string>>();

    string[] splitfile = wholefile.Split(';');
    foreach(string line in splitfile)
    {
      var section = new List<string>();

      string[] splitline = line.Split(',');
      foreach (string item in splitline)
      {
        section.Add(RemoveWhitespace(item));
      }
      file.Add(section);
    }

    //parse file
    var ddfile = new DDYFile();

    foreach (var s in file)
    {
      if(s[0] == "Site:Location")
      {
        ddfile.Site_Location.SiteName = s[1];
        ddfile.Site_Location.Latitude = Convert.ToDouble(s[2]);
        ddfile.Site_Location.Longitude = Convert.ToDouble(s[3]);
        ddfile.Site_Location.Gmt = Convert.ToDouble(s[4]);
        ddfile.Site_Location.Elevation = Convert.ToDouble(s[5]);
      }
      else if (s[0] == "SizingPeriod:DesignDay")
      {
        var dd = new DesignDay();
        dd.Name = s[1];
        dd.Month = TryToInt(s[2]);
        dd.DayOfMonth = TryToInt(s[3]);
        dd.DayType = s[4];
        dd.MaxTdb = TryToDouble(s[5]);
        dd.DailyTdbRange = TryToDouble(s[6]);
        dd.DailyTdbModifierType = s[7];
        dd.DailyTdbModifiedSchedule = s[8];
        dd.HumidityType = s[9];
        dd.TwbAtMaxTdb = TryToDouble(s[10]);
        dd.HumidityIndicatingDayScheduleName = s[11];
        dd.HumidityRatio = TryToDouble(s[12]);
        dd.Enthalpy = TryToDouble(s[13]);
        dd.DailyTwbRange = TryToDouble(s[14]);
        dd.BarometricPressure = TryToDouble(s[15]);
        dd.WindSpeed = TryToDouble(s[16]);
        dd.WindDirection = TryToDouble(s[17]);
        dd.Raining = TryToBool(s[18]);
        dd.Snowy = TryToBool(s[19]);
        dd.Dst = TryToBool(s[20]);
        dd.SolarModelIndicator = s[21];
        dd.BeamSolarDayScheduleName = s[22];
        dd.DiffuseSolarDayScheduleName = s[23];
        dd.OpticalDepthBeam = TryToDouble(s[24]);
        dd.OpticalDepthDiffuse = TryToDouble(s[25]);
        if(s.Count >= 27) dd.Clearness = TryToDouble(s[26]);

        ddfile.Design_Days.Add(dd);
      }
    }

    A = ddfile.Design_Days[y].ToListString();


  }

  // <Custom additional code> 

  public string RemoveWhitespace(string input)
  {
    return new string(input.ToCharArray()
      .Where(c => !Char.IsWhiteSpace(c))
      .ToArray());
  }

  public double TryToDouble(string input)
  {
    try
    {
      return Convert.ToDouble(input);
    }
    catch
    {
      return double.MinValue;
    }
  }

  public int TryToInt(string input)
  {
    try
    {
      return Convert.ToInt32(input);
    }
    catch
    {
      return Int32.MinValue;
    }
  }

  public bool TryToBool(string input)
  {
    try
    {
      return Convert.ToBoolean(input);
    }
    catch
    {
      return false; //should we always return false? or use a nullable type?
    }
  }

  public class DesignDay
  {
    public DesignDay(){}

    private string _name;
    private int _month;
    private int _dayOfMonth;
    private string _dayType;
    private double _maxTdb;
    private double _dailyTdbRange;
    private string _dailyTdbModifierType;
    private string _dailyTdbModifiedSchedule;
    private string _humidityType;
    private double _TwbAtMaxTdb;
    private string _humidityIndicatingDayScheduleName; //type unknown
    private double _humidityRatio;
    private double _enthalpy;
    private double _dailyTwbRange;
    private double _barometricPressure;
    private double _windSpeed;
    private double _windDirection;
    private bool _raining;
    private bool _snowy;
    private bool _dst; //daylight savings time
    private string _solarModelIndicator;
    private string _beamSolarDayScheduleName;
    private string _diffuseSolarDayScheduleName;
    private double _opticalDepthBeam;
    private double _opticalDepthDiffuse;
    private double _clearness;

    public string Name {get { return _name;} set {_name = value;}}
    public int Month { get { return _month; } set { _month = value;}}
    public int DayOfMonth{ get { return _dayOfMonth; } set { _dayOfMonth = value;}}
    public string DayType{ get { return _dayType; } set { _dayType = value;}}
    public double MaxTdb{ get { return _maxTdb; } set { _maxTdb = value;}}
    public double DailyTdbRange{ get { return _dailyTdbRange; } set { _dailyTdbRange = value;}}
    public string DailyTdbModifierType {get { return _dailyTdbModifierType;} set {_dailyTdbModifierType = value;}}
    public string DailyTdbModifiedSchedule{get { return _dailyTdbModifiedSchedule;} set {_dailyTdbModifiedSchedule = value;}}
    public string HumidityType{ get { return _humidityType; } set { _humidityType = value;}}
    public double TwbAtMaxTdb{get { return _TwbAtMaxTdb;} set {_TwbAtMaxTdb = value;}}
    public string HumidityIndicatingDayScheduleName{ get { return _humidityIndicatingDayScheduleName; } set { _humidityIndicatingDayScheduleName = value;}}
    public double HumidityRatio{ get { return _humidityRatio; } set { _humidityRatio = value;}}
    public double Enthalpy{ get { return _enthalpy; } set { _enthalpy = value;}}
    public double DailyTwbRange{ get { return _dailyTwbRange; } set { _dailyTwbRange = value;}}
    public double BarometricPressure{ get { return _barometricPressure; } set { _barometricPressure = value;}}
    public double WindSpeed{ get { return _windSpeed; } set { _windSpeed = value;}}
    public double WindDirection{ get { return _windDirection; } set { _windDirection = value;}}
    public bool Raining{ get { return _raining; } set { _raining = value;}}
    public bool Snowy{ get { return _snowy; } set { _snowy = value;}}
    public bool Dst{ get { return _dst; } set { _dst = value;}}
    public string SolarModelIndicator{ get { return _solarModelIndicator; } set { _solarModelIndicator = value;}}
    public string BeamSolarDayScheduleName{ get { return _beamSolarDayScheduleName; } set { _beamSolarDayScheduleName = value;}}
    public string DiffuseSolarDayScheduleName{ get { return _diffuseSolarDayScheduleName; } set { _diffuseSolarDayScheduleName = value;}}
    public double OpticalDepthBeam{ get { return _opticalDepthBeam; } set { _opticalDepthBeam = value;}}
    public double OpticalDepthDiffuse{ get { return _opticalDepthDiffuse; } set { _opticalDepthDiffuse = value;}}
    public double Clearness{ get { return _clearness; } set { _clearness = value;}}

    public override string ToString()
    {
      return "DesignDay item";
    }

    public List<string> ToListString()
    {
      var rtnlist = new List<string>();

      rtnlist.Add("Name: " + _name);
      rtnlist.Add("Month: " + _month.ToString());
      rtnlist.Add("DayOfMonth: " + _dayOfMonth.ToString());
      rtnlist.Add("DayType: " + _dayType.ToString());
      rtnlist.Add("MaxTdb: " + _maxTdb.ToString());
      rtnlist.Add("DailyTdbRange: " + _dailyTdbRange.ToString());
      rtnlist.Add("DailyTdbModifierType: " + _dailyTdbModifierType);
      rtnlist.Add("DailyTdbModifiedSchedule: " + _dailyTdbModifiedSchedule);
      rtnlist.Add("HumidityType: " + _humidityType);
      rtnlist.Add("TwbAtMaxTdb: " + _TwbAtMaxTdb.ToString());
      rtnlist.Add("HumidityIndicatingDayScheduleName: " + _humidityIndicatingDayScheduleName);
      rtnlist.Add("HumidityRatio: " + _humidityRatio.ToString());
      rtnlist.Add("Enthalpy: " + _enthalpy.ToString());
      rtnlist.Add("DailyTwbRange: " + _dailyTwbRange.ToString());
      rtnlist.Add("BarometricPressure: " + _barometricPressure.ToString());
      rtnlist.Add("WindSpeed: " + _windSpeed.ToString());
      rtnlist.Add("WindDirection: " + _windDirection.ToString());
      rtnlist.Add("Raining: " + _raining.ToString());
      rtnlist.Add("Snowy: " + _snowy.ToString());
      rtnlist.Add("Dst: " + _dst.ToString());
      rtnlist.Add("SolarModelIndicator: " + _solarModelIndicator);
      rtnlist.Add("BeamSolarDayScheduleName: " + _beamSolarDayScheduleName);
      rtnlist.Add("DiffuseSolarDayScheduleName: " + _diffuseSolarDayScheduleName);
      rtnlist.Add("OpticalDepthBeam: " + _opticalDepthBeam.ToString());
      rtnlist.Add("OpticalDepthDiffuse: " + _opticalDepthDiffuse.ToString());
      rtnlist.Add("Clearness: " + _clearness.ToString());
      return rtnlist;
    }
  }

  public class SiteLocation
  {
    public SiteLocation(){}

    private string _siteName;
    private double _latitude;
    private double _longitude;
    private double _gmt; //time zone
    private double _elevation;

    public string SiteName { get { return _siteName; } set { _siteName = value;}}
    public double Latitude { get { return _latitude; } set { _latitude = value;}}
    public double Longitude { get { return _longitude; } set { _longitude = value;}}
    public double Gmt { get { return _gmt; } set { _gmt = value;}}
    public double Elevation { get { return _elevation; } set { _elevation = value;}}

    public override string ToString()
    {
      return "SiteLocation " + _siteName;
    }
  }

  public class DDYFile
  {
    public DDYFile(){}

    private SiteLocation _siteLocation = new SiteLocation();
    private List<DesignDay> _designDays = new List<DesignDay>();

    public SiteLocation Site_Location { get { return _siteLocation; } set { _siteLocation = value;}}
    public List<DesignDay> Design_Days { get { return _designDays; } set { _designDays = value;}}

    public override string ToString()
    {
      return "DDY file for " + _siteLocation.SiteName + ", " + _designDays.Count.ToString() + " design days found";
    }
  }

  // </Custom additional code> 
}