Call Calendar from MDIChildFrame in 2.7

This should be an easy one, but calendars alone are tricky. I am just trying to call
a calendar frame from an MDIParentFrame (or MDIChildFrame under an
MDIParentFrame). The problem is that either the calendar frame cannot be
destroyed or Python hangs when we try to exit. ( MDI is used to try and “clean up”
all sub windows, frames, etc. since I was dealing with two calendar calls - A
Start_Date and an End_Date)
I have tried MDIClientWindow and Dialogs, but nothing closes cleanly in 2.7.
I am trying to avoid using Threads or any thing too complicated.
Thanks, Robin Randall

Below is my calendar code:

class MyCalendar(wx.Frame):
    def __init__(self, *args, **kargs):
        wx.Frame.__init__(self, *args, **kargs)
        self.cal = wx.calendar.CalendarCtrl(self, -1, wx.DateTime_Now())
        self.timer = wx.Timer(self)
        self.timer.Start(10000)
        self.Bind(wx.EVT_TIMER, self.update_date)

    def update_date(self, evt):
        date = wx.DateTime_Now()
        self.cal.SetDate(date)              # Just selects a single date to be used
                                                        # elsewhere in the program.

class MyApp(wx.App):
    def OnInit(self):
        return 1
  • By “2.7” do you mean Python 2.7 or wxPython 2.7 (Which is about a thousand years old) If the former then what version of wxPython are you using?
  • Please explain more about “nothing closes cleanly”
  • What is used as the parent window, if any?
  • What does the code look like where you create the MyCalendar? (Event better, can you provide a small runnable sample that demonstrates the problem?)
  1. Python 2.7
    1a. wx.Python 3.0
  2. Hangs. Usually I can do Ctrl-C and get back to Command Prompt. Sometime I get Window which says “python.exe has stopped working”
    “A problem caused the program to stop working correctly.
    Windows will close the program and notify you if a solution is available.”
  3. Parent(s) which are handlers from menu.
    #--------------------------------------------
    def home_billing_date(self, event):
    self.home_open(event)
    #Calendar code:
    #instanciate the class
    frame=MyCalendar(self, -1, size = (260, 280), pos=(500,250))
    frame.Show(1)
    #self.Destroy()
    #self.Close(True)
    app.MainLoop()
    #--------------------------------------------
    def home_system_date(self, event):
    self.home_open(event)
    #Calendar code:
    # instanciate the class
    frame=MyCalendar(self, size = (260, 280), pos=(500,250))
    frame.Show(1)
    #self.Destroy()
    #self.Close(True)
    app.MainLoop()
    #--------------------------------------------
    Grand Parent:

This is the main frame where all the widgets are added

class MyMDIFrame(wx.MDIParentFrame):
def init(self):
global frame
frame=wx.MDIParentFrame.init(self, None, -1, “Billing ver. 1.0”, size=(1000, 600) )
self.sizeH = 250 # For Toolbar size
self.log= open(“Notepad–.log”,‘w+’)
#sizer=wx.BoxSizer(wx.VERTICAL)
self.shellName=‘frame’
# Setup icon on title bar
#_icon = wx.Icon(“billing.ico”, wx.BITMAP_TYPE_ICO)
#self.SetIcon(_icon)
self.config_Dates= {‘MDY’:’%A, %m/%d/%y ‘, ‘YMD’:’%A, %y/%m/%y ‘, ‘DMY’:’%A, %d/%m/%y ‘}
#===============================================================
self.edit_ctrl = wx.TextCtrl(self, -1, “”, pos=(700,10), size=(150, 40))
#self.edit_ctrl.SetDefaultStyle(wx.TextAttr(wx.NullColour, wx.LIGHT_GREY))
self.edit_ctrl.SetBackgroundColour(“grey”)
self.MenuTitle = wx.TextCtrl(self, -1, " “, pos=(700,10), size=(150, 40))
self.MenuTitle.SetValue(”")
self.edit_ctrl.SetBackgroundColour(“grey”)
self.fcurClr=self.edit_ctrl.GetForegroundColour()
self.bcurClr=self.edit_ctrl.GetBackgroundColour()
self.curFont = wx.Font(20, wx.MODERN, wx.NORMAL, wx.NORMAL, False, u’Consolas’)
self.edit_ctrl.SetFont(self.curFont)
:
:
4. MyCalendar runs fine by itself. But when you add the handler, the frame above and the frame above that I can’t get it to run. I was hoping
wx.MDIParentFrame ->wx.MDIChildFrame -> MyCalendar(wx.Frame) -> wx.calendar.CalendarCtrl()
would work. But I think I have given you:
wx.MDIParentFrame ->wx.Frame-> MyCalendar(wx.Frame) ->
wx.calendar.CalendarCtrl()
which displays the calendar but hangs upon closing. BTW, I have gotten Grids and Dialogs to close just fine when I close the MDIParent Window…
Thanks,
Robin Randall

First thing that jumps out at me is that you don’t need to call app.MainLoop again. The new frame(s) will have events dispatched to them from main loop you started at the beginning of the application. By calling it again you are effectively blocking the event handlers where you call it from being able to return to the real main loop until the nested main loop exits.

I also thought of that but then I could not test the calendar by itself.
I will try removing “app.MainLoop” statements in “Home_Billing_Date” & “Home_System_Date” methods. Let me know if you see anything else obvious? Thanks. RR

It is better. But in 2.7 how do you get MyCalendar to close after you have selected a date?
Right now it appears to go away because I do not have ALWAYS_ON_TOP, but it is still showing underneath the Main Frame (For the number of seconds I have set). It goes away if I press [X] in right hand corner of the calendar window, of course.

PS If you are interested in the code that solves the problem of incrementing/decrementing the Year when the Month goes past 12 or less than 1, I would be glad to include it in my post. But some may not want that. When incrementing the Year, I leave the Month & Day alone (to see how holidays and leap years change things) I would add it to GitHub if
I was more familiar. Oh, I also changed the calendar Year Range default to
(1900-2100) for Demo code. Now that my code increments the Year, this is more important.

# experiment with wxPython's wx.lib.calendar
# show weekends and holidays in different colour
# tested with Python25 and wxPython28   vegaseat   31jul2007
##########################################################################
# Modification Log
#-------------------------------------------------------------------------
# Date       Changes
#-------------------------------------------------------------------------
# 08/01/2020 Modified by Robin Randall
# 08/15/2020 Fixed Year bug by changing range to (1980, 2100
# 08/16/2020 Removed "PySimple.py" bug
# 08/20/2020 Failed to get MDI working with popup calendar
# 08/20/2020 Failed to change Current Day highlighting to Black & Yellow (from Black & 'Blue)
# 08/22/2020 Added code to allow Month spinner to also control Year spinner. Year spinner does not affect Month
##########################################################################
import wx
import wx.lib.calendar
import wx.calendar

class Calendar_Panel(wx.Panel):
#class Calendar_Frame(wx.Frame):
    """ create a panel with a calendar on it"""
    def __init__(self, parent, id):
        # create a panel
        wx.Panel.__init__(self, parent, id)
        #wx.Frame.__init__(self, *args, **kargs)
        # create a label on the panel
        self.label1 = wx.StaticText(self, id, "", wx.Point(25, 220))
        # create the calendar
        self.cal = wx.lib.calendar.Calendar(self, id, pos=(25, 35), size=(200, 180))
        #self.cal = wx.calendar.CalendarCtrl(self, -1, wx.DateTime_Now(), pos=(25, 35), size=(200, 180))
        # get the current month & year
        start_month = self.cal.GetMonth()
        start_year = self.cal.GetYear()
        start_day  = self.cal.GetDay()
        self.cal.SetCurrentDay()
        # mouse click on a day
	self.Bind(wx.lib.calendar.EVT_CALENDAR, self.OnCalSelected)

        text = "Current Date = %02d/%02d/%d" % (start_month, start_day, start_year)
        self.label1.SetLabel(text)
        
        # set some of the colours
        self.SetBackgroundColour("yellow")
        self.cal.SetWeekColor('white', 'blue')
        self.cal.SetColor(wx.lib.calendar.COLOR_WEEKEND_FONT, 'black')
        self.cal.SetColor(wx.lib.calendar.COLOR_WEEKEND_BACKGROUND, 'green')
        self.cal.ShowWeekEnd()
        # colour holidays of current month
        self.set_days = holidays[start_month]
        self.cal.AddSelect(self.set_days, 'red', 'white')
        self.cal.Refresh()
        
        # mouse click on a day
        self.Bind(wx.lib.calendar.EVT_CALENDAR, self.OnCalSelected)
        # create year spin
        self.texty = wx.TextCtrl(self, -1, str(start_year), pos=(25, 10), size=(50, -1))
        h = self.texty.GetSize().height
        self.spiny = wx.SpinButton(self, -1, pos=(70, 10), size=(h*2, h))
        #Allowed for wider Year Range
        self.spiny.SetRange(1900, 2100)
        self.spiny.SetValue(start_year)
        self.Bind(wx.EVT_SPIN, self.OnSpiny, self.spiny)
        # create month spin
        self.textm = wx.TextCtrl(self, -1, str(start_month), pos=(130, 10), size=(50, -1))
        h = self.textm.GetSize().height
        self.spinm = wx.SpinButton(self, -1, pos=(170, 10), size=(h*2, h))
        #New Range allows Month spinner to also set Year
        self.spinm.SetRange(0, 13)
        self.spinm.SetValue(start_month)
        self.Bind(wx.EVT_SPIN, self.OnSpinm, self.spinm)
        
        self.timer = wx.Timer(self)
	self.timer.Start(1000)
	self.Bind(wx.EVT_TIMER, self.update_date)
	
    def update_date(self, evt):
	date = wx.DateTime_Now()
        self.cal.SetDate(date)    
        
        
    def OnCalToday(self, evt):
        self.cal.SetHighlightColours(wx.lib.calendar.start_day, 'black', 'yellow')
        
    def OnCalSelected(self, evt):
        text = "Date selected = %02d/%02d/%d" % (evt.month, evt.day, evt.year)
        self.label1.SetLabel(text)
        
    def OnSpiny(self, event):
        year = event.GetPosition()
        self.texty.SetValue(str(year))
        self.cal.SetYear(year)
        self.ResetDisplay()
        
    def OnSpinm(self, event):
        month = event.GetPosition()
        # Below code added so month spinner sets the year
        year = self.cal.GetYear()
        if (self.spinm.GetValue() == 13):
	            month = 1
	            year = year + 1
	if (self.spinm.GetValue() == 0):
	            month = 12
	            year = year - 1 
	self.texty.SetValue(str(year))
	self.cal.SetYear(year)
        self.spinm.SetValue(month)
        self.textm.SetValue(str(month))
        self.cal.SetMonth(month)
        self.ResetDisplay()
        
    def ResetDisplay(self):
        # reset holiday colour
        self.cal.AddSelect(self.set_days, 'black', 'white')
        # get number of the month
        month = self.cal.GetMonth()
        set_days = holidays[month]
        # set new holiday colour
        self.cal.AddSelect(set_days, 'red', 'yellow')
        self.cal.Refresh()
        # keep present list to reset colour
        self.set_days = set_days
        
# fill these dates out for the present year
# some could change with another year
# {month: list of holiday dates in month}
holidays = {
1: [1],
2: [13],
3: [22],
4: [3],
5: [29],
6: [15],
7: [4, 11],
8: [],
9: [3],
10: [],
11: [27, 26],
12: [24, 25]
}
class MyApp(wx.App):
    def OnInit(self):
        return 1
if __name__ == '__main__':
    app = MyApp(0)
# create a window/frame, no parent, -1 is default ID
frame = wx.Frame(None, -1, "Simple calendar", size = (260, 280))
#frame=wx.MDIParentFrame(None, -1, "MDI Parent", size = (260,280)) 
#instanciate the class
Calendar_Panel(frame, -1)
#   frame=Calendar_Frame(None)
frame.Show(1)
#   frame.Show()
app.MainLoop()

Call the frame’s Close method in the selection event handler.

Hi Robin,
I have tried self.Destroy & self.Close at the end of each handler. In either case, I believe I see a flash and calendar is gone.

I need it to stay long enough to make a date selection. And then I want it to disappear. It is OK if it waits for me to manually close it, since I might make a mistake.

But it seems like a wasted keystroke to me.

I don’t know about you, but my desired user experience seems well within the reasons you would develop such a widget.

I see 2 main uses:

  1. A static calendar to look at showing year, month, week, day at a time.

  2. Allowing a user to select a day, hour, minute from a selected month, year combination.

Robin

It sounds like what you really want is a wx.Dialog with the calendar widget and Ok and Cancel buttons.

Thanks Robin. Gee, wouldn’t it be nice if some of the sample calendar apps and demos stressed that wx.Dialog was often successful. Can’t you just write a memo and get your minions to take care of it? lol! It is probably done in Python 3, but if not, add/modify the demos. Demos is where I always go first. It is so wonderful to see half my code written already! You tell me where the future is. should I stop wx.Python and start learning Ktinker? If we had a tool that could build code like MineCraft and all the permutations would be possible and it would put it together in the most efficient manner. And you could run it along the way with never an error message or hang. Maybe someone like you could throw some brilliant ideas out there. Maybe you could be a Software Elon Musk, only better. I was in SQA for 30 years and with 9 years of Python I only got to do Test Automation. I would be glad to QA any ideas you have so we could go from prototypes to working systems in no time. Just let me know how I could help you best.

Robin, Thanks so much for all your effort in making Python the best language out there and doing the support work too!

You are amazing! and I hope you are rewarded in this life and the next! God Bless You!

Robin Randall

Thanks Robin. Gee, wouldn’t it be nice if some of the sample calendar apps and demos stressed that wx.Dialog was often successful. Can’t you just write a memo and get your minions to take care of it? lol! It is probably done in Python 3, but if not, add/modify the demos. Demos is where I always go first. It is so wonderful to see half my code written already! You tell me where the future is. should I stop wx.Python and start learning Ktinker? If we had a tool that could build code like MineCraft and all the permutations would be possible and it would put it together in the most efficient manner. And you could run it along the way with never an error message or hang. Maybe someone like you could throw some brilliant ideas out there. Maybe you could be a Software Elon Musk, only better. I was in SQA for 30 years and with 9 years of Python I only got to do Test Automation. I would be glad to QA any ideas you have so we could go from prototypes to working systems in no time. Just let me know how I could help you best.

Robin, Thanks so much for all your effort in making Python the best language out there and doing the support work too!

You are amazing! and I hope you are rewarded in this life and the next! God Bless You!

Robin Randall

Phone: 650-771-5064
Email: RobinLRandall@gmail.com
Sunnyvale, CA 94087


Robin

    September 4

It sounds like what you really want is a wx.Dialog with the calendar widget and Ok and Cancel buttons.


Visit Topic or reply to this email to respond.

To unsubscribe from these emails, click here.

I meant STAY_ON_TOP in previous email.

One thing I was not sure about was if I need to ADD calendar to a box_sizer to make it show in a wx.Dialog.

I just can’t find any case of using wx.MDIParentFrame to call

wx.MDIChild to call

wx.Dialog to call

wx.calendar (or other 2.7 cal object)

It took me a while to get the calendar working the way I wanted.

So I would think

class CalDialog(wx.Dialog)

would have the following:

  • wx.CLOSE as default

  • Title to show type of Date (may be part of dialog, calendar or separate wx.TextCtrl)

  • Call to wx.calendar object

  • wx.OK button to process new Date

  • wx.CANCEL button to forget change

  • self.Destroy() if CLOSE or CANCEL is clicked.

Robin

I think you are over-thinking things a bit, and maybe running yourself in circles. I suggest taking a step back and just learning and experimenting a bit with dialogs in general rather than your specific use case.

Dialog basics

  • You can use the TestDialog class in the Dialog sample in the demo as an example or a starting point. Eventually you can replace the static text and text controls on the dialog with the calendar control and whatever else you need, and adjust the sizers as needed to manage the layout how you want it. You can ignore the things related to the help provider and context help in that sample for now.

  • If your ok and cancel buttons have the wx.ID_OK and wx.ID_CANCEL IDs then you don’t need to worry about code to close the dialog. There are default event handlers for those IDs which will do a few things internally and then call EndModal. ShowModal will return one of those values so you can tell which button was clicked and respond accordingly.

  • If you want to allow some other way to exit the dialog (like when a date is double clicked in the calendar for example) then your code should also call EndModal to do it. The value passed to EndModal is what will be returned from ShowModal in the caller. You can reuse wx.ID_OK for that value, if it makes sense.

  • Be sure to call SetDefault on the Ok button so it will respond if you press ENTER while in the dialog. There is also built-in behavior for pressing the ESC key. In that case it will work the same as if the Cancel button was clicked.

  • A dialog should never destroy itself. Instead Destroy should be called from where the dialog was launched from, after any data provided by the dialog has been fetched and dealt with. If you like Python’s context manager feature then a dialog can be used as a context manager which will automatically call Destroy when the context is exited. Like this:

    with MyDialog(self, foo, bar) as dlg:
        if dlg.ShowModal() == wx.ID_OK:
            # do something with dlg values
  • The wx.StdDialogButtonSizer class is a handy way to make sure that the standard buttons on a dialog are ordered and layed out in a way that conforms with the actual platform’s guidelines. It’s not required to use it, but it’s there if you want it.

More Examples:

Robin, Thanks so much! I tried to google for something to show “under the bonnet” info, but just kept getting the same old docs with only the calls & parms. Using “self” often throws me because it is used as the first parm. Sometimes I wish there was only one way to do something, if that makes sense.
RR

Ive played with Test_Dialog but cannot for the life of me make the dialog size larger. Always about 300x300.

I have tried SetMinSize() and SetSizerAndFit(), but those do not make a larger dialog.

When I add the calendar as:

self.cal = wx.lib.calendar.Calendar(self, id, pos=(25, 35), size=(200, 180))

I get error:

Traceback (most recent call last):
File "C:\Users\Rob\Billing2\Dialog007.py", line 198, in OnButton
    dlg = TestDialog(self, -1, "Billing Date", pos=(200,200), size=(500, 500))
  File "C:\Users\Rob\Billing2\Dialog007.py", line 150, in __init__
    self.cal = wx.lib.calendar.Calendar(self, id, pos=(25, 35), size=(200, 180))
  File "C:\Python27\lib\site-packages\wx-3.0-msw\wx\lib\calendar.py", line 695, in __init__
    wx.PyControl.__init__(self, parent, id, pos, size, style | wx.WANTS_CHARS, validator, name)
  File "C:\Python27\lib\site-packages\wx-3.0-msw\wx\_controls.py", line 5841, in __init__
    _controls_.PyControl_swiginit(self,_controls_.new_PyControl(*args, **kwargs))
TypeError: in method 'new_PyControl', expected argument 2 of type 'int'

It just seems so hard to change size of wx.Dialog.

Robin

Phone: 650-771-5064
Email: RobinLRandall@gmail.com
Sunnyvale, CA 94087

What is id? It should be an integer.

As for the size and layout issues, it depends on how you set up your sizers, how items are added to the sizers, and how the widgets involved calculate their own best or minimum sizes. A good way to troubleshoot layout issues is to use the WIT, however it’s not easy to use with dialogs, so you may want to temporarily switch your Dialog to be a Frame instead, and then switch it back once you have the layout figured out.

Another good way to figure out how layout works is to use a tool like wxGalde or wxFormBuilder to make something like what you want and then examine the code they generate. You could either use that code directly, or just learn from it and then apply that knowledge to your hand-written code.

If you haven’t already you may want to read this wiki page about the various kinds window sizes. It’s probably a bit out of date, but it’s still good stuff to know.