How to create a splash screen while loading (Phoenix)
Keywords : Splashscreen, Loading, Timeout.
Contents
Introduction
AdvancedSplash is an easy way to add a splash screen to a wxPython application, but its use is not without pitfalls.
One of those is that programmers typically include a splash screen when their program takes a long time to load. This gives the user some feedback so they know that something is happening and that they shouldn't try to launch the application again.
The problem you will face, however, is that a long load time typically means a long time before the program execution gets to the App.MainLoop() command; and until the MainLoop is entered, the splash screen will not display correctly.
There are a lot of ways to tackle this problem. The simplest is probably to do as much initialization as possible in a function launched with a wx.CallLater call (wx.CallAfter doesn't seem to work from an OnInit function). Unfortunately, you may still face delays (such as long loads during imports) and the user interface will not be responsive until your initializer completes (so the user will not be able to click on the splash to dismiss it).
Resist the temptation to put your intialization in a background thread! This is valid only if no wx calls are made in the initialization. Never put any wx call (besides wx.CallAfter) in a background thread.
Another solution is to fork the program's execution. Display the splash screen in one fork and launch the application in the other.
Note: The following example has been written for Windows. There's no fork command in the Windows version of Python, so I'll be using spawnl instead. Feel free to post a Linux/Mac version of the following.
Demonstrating :
Tested py3.x, wx4.x and Win10.
Are you ready to use some samples ?
Test, modify, correct, complete, improve and share your discoveries !
Example
The following example simulates 1.3 seconds of delay in doing all of my imports and 6 seconds of delay in loading my objects from the xrc file. These numbers were taken from an actual application.
Note how I put the splash screen code at the top of the file. This lets me get it done when only a minimal set of libraries are loaded.
ICON file : icon_wxWidgets.ico
PNG file : splash.png
1 # sample_one.py
2
3 import os
4 import sys
5 import wx
6 import wx.lib.agw.advancedsplash as AS
7
8 SHOW_SPLASH = True
9 SPLASH_FN = "splash.png"
10 SPLASH_TIME = 5000
11
12 # class MyFrame
13 # class MyApp
14
15 #-------------------------------------------------------------------------------
16
17 # Test to see if we need to show a splash screen.
18 # If the splash is enabled (and we're not the application fork),
19 # then show a splash screen and relaunch the same application
20 # except as the application fork.
21
22 if __name__ == "__main__":
23 AppFN = sys.argv[0]
24
25 if SHOW_SPLASH and (len(sys.argv) == 1) and AppFN.endswith(".exe"):
26 App = wx.App()
27
28 #--
29
30 bitmap = wx.Bitmap(SPLASH_FN, wx.BITMAP_TYPE_PNG)
31 shadow = wx.RED
32
33 frame = AS.AdvancedSplash(None,
34 bitmap=bitmap,
35 timeout=SPLASH_TIME,
36 agwStyle=AS.AS_TIMEOUT |
37 AS.AS_CENTER_ON_PARENT |
38 AS.AS_SHADOW_BITMAP,
39 shadowcolour=shadow)
40
41 #--
42
43 os.spawnl(os.P_NOWAIT,
44 AppFN,
45 '"%s"' % AppFN.replace('"', r'\"'),
46 "NO_SPLASH")
47
48 #--
49
50 App.MainLoop()
51 sys.exit()
52
53 #-------------------------------------------------------------------------------
54
55 import time
56
57 # Simulate 1.3s of time spent importing libraries and source files.
58 time.sleep(1.3)
59
60 #---------------------------------------------------------------------------
61
62 class MyFrame(wx.Frame):
63 """
64 ...
65 """
66 def __init__(self):
67 super(MyFrame, self).__init__(None,
68 -1,
69 title="")
70
71 #------------
72
73 # Return application name.
74 self.app_name = wx.GetApp().GetAppName()
75
76 #------------
77
78 # Simplified init method.
79 self.SetProperties()
80 self.CreateCtrls()
81 self.BindEvents()
82 self.DoLayout()
83
84 #------------
85
86 self.CenterOnScreen()
87
88 #-----------------------------------------------------------------------
89
90 def SetProperties(self):
91 """
92 ...
93 """
94
95 self.SetTitle(self.app_name)
96
97 #------------
98
99 frameicon = wx.Icon("icon_wxWidgets.ico")
100 self.SetIcon(frameicon)
101
102
103 def CreateCtrls(self):
104 """
105 ...
106 """
107
108 # Create a panel.
109 self.panel = wx.Panel(self, -1)
110
111 #------------
112
113 # Add a button.
114 self.btnClose = wx.Button(self.panel,
115 -1,
116 "&Close")
117
118
119 def BindEvents(self):
120 """
121 Bind some events to an events handler.
122 """
123
124 # Bind the close event to an event handler.
125 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
126
127 # Bind the button event to an event handler.
128 self.Bind(wx.EVT_BUTTON, self.OnCloseMe, self.btnClose)
129
130
131 def DoLayout(self):
132 """
133 ...
134 """
135
136 # MainSizer is the top-level one that manages everything.
137 mainSizer = wx.BoxSizer(wx.VERTICAL)
138
139 # wx.BoxSizer(window, proportion, flag, border)
140 # wx.BoxSizer(sizer, proportion, flag, border)
141 mainSizer.Add(self.btnClose, 1, wx.EXPAND | wx.ALL, 10)
142
143 # Finally, tell the panel to use the sizer for layout.
144 self.panel.SetAutoLayout(True)
145 self.panel.SetSizer(mainSizer)
146
147 mainSizer.Fit(self.panel)
148
149 #-----------------------------------------------------------------------
150
151 def OnCloseMe(self, event):
152 """
153 ...
154 """
155
156 self.Close(True)
157
158
159 def OnCloseWindow(self, event):
160 """
161 ...
162 """
163
164 self.Destroy()
165
166 #---------------------------------------------------------------------------
167
168 class MyApp(wx.App):
169 """
170 ...
171 """
172 def OnInit(self):
173
174 #------------
175
176 self.SetAppName("Main frame")
177
178 #------------
179
180 # Simulate 6s of time spent initializing wx components.
181 time.sleep(6)
182
183 #------------
184
185 self.frame = MyFrame()
186 self.frame.Show(True)
187
188 return True
189
190 #---------------------------------------------------------------------------
191
192 def main():
193 app = MyApp(False)
194 app.MainLoop()
195
196 #---------------------------------------------------------------------------
197
198 if __name__ == "__main__" :
199 main()
Distributing application
Here's a "setup.py" file I used to compile it to an ".exe" :
SETUP file : setup.py
1 # setup.py
2
3 #-------------------------------------------------------------------------------
4 # Import cx_Freeze packages.
5 #-------------------------------------------------------------------------------
6
7 from cx_Freeze import setup, Executable
8
9
10 # Dependencies are automatically detected, but it might need fine tuning.
11 options = {"packages": [], "excludes": ["tkinter"]}
12
13 #-------------------------------------------------------------------------------
14 # Application Information - Create setup.
15 #-------------------------------------------------------------------------------
16
17 setup(name="sample_one" ,
18 version="0.1" ,
19 description="Test",
20 author="wxPython",
21 options={"build_exe": options},
22 executables = [Executable(script="sample_one.py",
23 base="Win32GUI",
24 icon="icon_wxWidgets.ico")])
Download source
Additional Information
Link :
https://wiki.wxpython.org/Splash%20screen%20%28Phoenix%29
https://wiki.wxpython.org/Customized%20splash%20screen%20%28Phoenix%29
- - - - -
https://wiki.wxpython.org/TitleIndex
Thanks to
??? (sample_one.py coding), the wxPython community...
About this page
Date (d/m/y) Person (bot) Comments :
12/06/18 - Ecco (Updated page and modified example for wxPython Phoenix).
Comments
- blah, blah, blah....