You better use your desktop PC to check emails

I always think that on a mobile device you are more vulnerable to become a victim of malicious emails.

Most of the time I check my emails on my desktop or laptop computer. This weekend I was sitting on a chair outside on my patio and used a mobile device to check my emails, using Outlook on Android, to get into the details.

On my email account all incoming email from people I don’t know ends up in my junk folder, but I usually check it quickly to find emails from sender I don’t know (yet) which might not be junk emails. This one caught my attention:


After a first look it did not look suspicious to me: the email address looked authentic, thus I opened the attached pdf file. It contained some letter telling me about a game I had purchased from the Apple store ( a game I never heard of ) and that $ 50 would be paid from my credit card account if I would not cancel that order; a link followed to do so.

I stopped there, left my chair, and checked the very same email on my Windows computer, using Outlook. Here is how that email looked like there:


Very different, I would think: it was obvious right away that this is a suspicious email address not owned by Apple, and Outlook actually did not even allow me to open the attachment while that email was sitting in my junk folder.

I was really surprised to see one and the same app – Microsoft Outlook – behave that different on two different platforms. While it did a quiet good job to protect me on Windows from this suspicious and most likely malicious email it did a lousy job on Android.

On a desktop PC you always have the chance to move your mouse pointer over a link to see how it resolves. On a mobile platform like Android you can’t do that: touching a link will open it.

That said, and this experience having made, I stick to my opinion that it is much safer to do email on a desktop computer than on a mobile device.



Base code to make context menu work with wxPython CustomTreeCtrl

After it took me an hour or two to figure that out, here is some base Python code snippet showing how to make context menues work with wxPython’s CustomTreeCtrl:

class CustomTreeCtrl(CT.CustomTreeCtrl):

    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,

        CT.CustomTreeCtrl.__init__(self, parent, id, pos, size, style, agwStyle)
        # ...        
        self.item = None
        self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
    def OnRightDown(self, event):

        pt = event.GetPosition()
        item, flags = self.HitTest(pt)

        if item:
            self.item = item    
    def OnContextMenu(self, event):
        # Setup right-click menu for tree items
        menu = wx.Menu()
        treeMenuItem1 = menu.Append(wx.ID_ANY, "Menu Entry 1")
        treeMenuItem2 = menu.Append(wx.ID_ANY, "Menu Entry 2")
        self.Bind(wx.EVT_MENU, self.OnMenuEntry1, treeMenuItem1)
        self.Bind(wx.EVT_MENU, self.OnMenuEntry2, treeMenuItem2)
    def OnMenuEntry1(self, event):
        print "Method OnMenuEntry1 not implemented yet."
    def OnMenuEntry2(self, event):
        print "Method OnMenuEntry2 not implemented yet."

In essence:

  • we need a method to capture what item has been selected in a tree. This is done by OnRightDown() and the HitTest() method. We store the selected item in an attribute of our CustomTreeCtrl object: self.item
  • we need a method to display the context menu: OnContextMenu()
  • we need one method per context menu entry, yet to be implemented: OnMenuEntry1(), onMenuEntry2(), …
  • we need to ensure the proper bindings so that all these methods working, thus:
    • binding wx.EVT_CONTEXT_MENU to OnContextMenu() in the constructor of our CustomTreeCtrl object
    • binding wx.EVT_RIGHT_DOWN to OnRightDown() in the constructor of our CustomTreeCtrl object
    • binding wx.EVT_MENU to every method used to handle clicking a menu entry, like OnMenuEntry1(), onMenuEntry2(), in OnContextMenu()

wxGlade and Accelerator Keys

Currently I am using wxGlade 0.8.3 and wxPython 4.0.3 and I observe a strange behavior when using accelerator keys for menus.

In wxGlade I have to define them using a double back-slash, otherwise it won’t work:

wxGlade Accelerator Keys

The weird outcome is, my GUI afterwards looks like this:

wxPython menu with Accelerator Keys
Thus I need this Python code to fix my menu entries:

for mi in self.GetMenuBar().GetMenu(0).GetMenuItems():
  txt = mi.GetItemLabel()
  if "\\" in txt:


There is not such a thing as a stupid question

I hope we all agree on this: “There is not such a thing as a stupid question”. Only stupid answers exist a lot.

imageAnyway, my first reaction when reading that question on was: what a stupid question: “Can you write a program for adding 10 numbers?” !

I am still not sure though what the intension was behind that question, nevertheless it yielded a good and interesting discussion thread

  • with a lot of code samples
  • with a lot of fun, see this answer or this one
  • with some interesting discussion about how to phrase useful requirements for software development ( “what did he mean by ‘adding’ !”, see this answer )
  • with some interesting way to deal with those requirements: instead of firing up your IDE right away, think how else the problem can be soled; see this answer.

Thus, nice evidence that every question makes some sense, no matter how stupid it sounds initially.

image from pexels


How to manually update VirtualBox Guest Addons

After upgrading VirtualBox on your system it is highly recommended to also update VirtualBox Guest Addons for your guest systems.
This should work automatically but typically doesn’t, at least in my case; just happened to me again after upgrading to VirtualBox 5.2.8.

How to update VirtualBox Guest Addons is described here:

In the “Devices” menu in the virtual machine’s menu bar, VirtualBox has a handy menu item named “Insert Guest Additions CD image”, which mounts the Guest Additions ISO file inside your virtual machine.

This did not work in my case: when clicking “Insert Guest Additions CD image” I get an error message which is not very specifc, it just says the desired CD image could not be mounted. A closer looks reveals that actually that image is mounted already, it just did not start automatically to perform the update.

Here is what I do in this case:

  1. Open a termin window
  2. Type ‘df -h’ to find the path to my mounted CD image. In my case that entry looks like this:
    /dev/sr0 56M 56M 0 100% /media/amagard/VBox_GAs_5.2.8
  3. cd to this path ( here: /media/amagard/VBox_GAs_5.2.8 )
  4. Type ‘sudo ./’

This actually kicked off the installation of the VirtualBox Guest Addons.

Jupyter: Plotting pivots & changing legend entries

A while ago I blogged about project Jupyter and in the last days I have been working a lot with it and I am still fascinated by its power.

Today I faced and solved two challenges I like to share here:
. plotting a pivot table
. changing legend entries

Assume we have the following dataframe:

Creating a pivot is a piece of cake by using the pandas pivot_table method on that dataframe:

pivot = pd.pivot_table(df,index=["Org"],values=["Male employees","Female employees"], 


This gets us
. the number of departments per org ( = len Female employees or len Male employees )
. the sum of male and female employees per org ( = sum Female employees and sum Male employees )
. as well as mean, min and max

How to plot ?
We can simply save the pivot tables as a new dataframe ‘pivot’ and call its plot method. Let’s say we want to plot sum of male and female employees per org. First we need to drop the other statistics from the pivot table we don’t need for the plot. Then we plot:




Only problem here is that the legend entries of this plot look a bit cryptic. Here is some code to fix this:


ax = plt.gca() 
handles,labels = ax.get_legend_handles_labels() 
new_labels = [] 
for l in labels: 
ax.legend(handles, new_labels)


I have shared the entire notebook here.

How to print ipython notebooks without the source code

This is something I really need to create sort of standard reports based on ipython notebooks which should not contain the source code and input prompts of ipython cells: the capability to print ipython notebooks without the source code.

There are ways to do that as discussed here on stackoverflow but all these methods involve adding some ugly code to your ipython cells or tweaking the way the ipython server is started ( or running nbconvert ) which might be out of your control if you use some cloud offering like Data Science Experience on IBM Cloud and not your own ipython installation.

Here is how I achieve this:

I simply download my notebook as html.

Then I run this python script to convert that html file so that prompts and code cells are gone:

FILE = "/somewhere/myHTMLFile.html"

with open(FILE, 'r') as html_file:
    content =

# Get rid off prompts and source code
content = content.replace("div.input_area {","div.input_area {\n\tdisplay: none;")    
content = content.replace(".prompt {",".prompt {\n\tdisplay: none;")

f = open(FILE, 'w')

That script bascially adds the CSS ‘display: none’ attribute for all divs of class ‘prompt’ or ‘input_area’.

That tweaked html page now easily can be printed into a pdf file for me to get my standard report without any code or input prompt cells.

If you know what you are doing you can add more CSS tweaking, like e.g. this one, to that Python code:

# For dataframe tables use Courier font family with smaller font size
content = content.replace(".dataframe thead","table.dataframe { font-size: 7px; font-family: Courier; }\n.dataframe thead")

To figure out things like that I used Firefox Inspector to determine class names of DOM elements ( like e.g. ‘div.data_frame’ is used to display dataframe tables in ipython ) and some CSS knowledge to achieve the manipulations I find useful, like reducing the font size of tables in order to make them fit on pages printed with portrait orientation.