source: sasview/src/sas/sasgui/guiframe/gui_statusbar.py @ d0ce666f

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249unittest-saveload
Last change on this file since d0ce666f was 463e7ffc, checked in by Ricardo Ferraz Leal <ricleal@…>, 7 years ago

getLogger with module name

  • Property mode set to 100644
File size: 14.9 KB
Line 
1"""
2Defines and draws the status bar that should appear along the bottom of the
3main SasView window.
4"""
5import wx
6import sys
7import logging
8import datetime
9from wx import StatusBar as wxStatusB
10from wx.lib import newevent
11import wx.richtext
12from sas.sasgui.guiframe.gui_style import GUIFRAME_ICON
13
14logger = logging.getLogger(__name__)
15
16# Number of fields on the status bar
17NB_FIELDS = 4
18#position of the status bar's fields
19ICON_POSITION = 0
20MSG_POSITION = 1
21GAUGE_POSITION = 2
22CONSOLE_POSITION = 3
23BUTTON_SIZE = 40
24STATUS_BAR_ICON_SIZE = 12
25CONSOLE_WIDTH = 500
26CONSOLE_HEIGHT = 300
27
28if sys.platform.count("win32") > 0:
29    FONT_VARIANT = 0
30else:
31    FONT_VARIANT = 1
32
33GREEN = wx.Colour(95, 190, 95)
34YELLOW = wx.Colour(247, 214, 49)
35RED = wx.Colour(234, 89, 78)
36
37class ConsolePanel(wx.Panel):
38    """
39    Interaction class for adding messages to the Console log.
40    """
41    def __init__(self, parent, *args, **kwargs):
42        wx.Panel.__init__(self, parent=parent, *args, **kwargs)
43        self.parent = parent
44        self.sizer = wx.BoxSizer(wx.VERTICAL)
45
46        self.msg_txt = wx.richtext.RichTextCtrl(self, size=(CONSOLE_WIDTH-40,
47                                                CONSOLE_HEIGHT-60),
48                                   style=wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER)
49
50        self.msg_txt.SetEditable(False)
51        timestamp = datetime.datetime.now()
52        status = '{:%Y-%m-%d %H:%M:%S} : No message available'.format(timestamp)
53        self.msg_txt.SetValue(status)
54        self.sizer.Add(self.msg_txt, 1, wx.EXPAND|wx.ALL, 10)
55        self.SetSizer(self.sizer)
56
57    def set_message(self, status="", event=None):
58        """
59        Adds a message to the console log as well as the main sasview.log
60
61        :param status: A status message to be sent to the console log.
62        :param event: A wx event.
63        """
64        status = str(status)
65        if status.strip() == "":
66            return
67        # Add timestamp
68        timestamp = datetime.datetime.now()
69        status = '{:%Y-%m-%d %H:%M:%S} : '.format(timestamp) + status
70        color = (0, 0, 0) #black
71        icon_bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_TOOLBAR)
72        if hasattr(event, "info"):
73            icon_type = event.info.lower()
74            if icon_type == "warning":
75                logger.warning(status)
76                color = (0, 0, 255) # blue
77                icon_bmp = wx.ArtProvider.GetBitmap(wx.ART_WARNING,
78                                                    wx.ART_TOOLBAR)
79            if icon_type == "error":
80                logger.error(status)
81                color = (255, 0, 0) # red
82                icon_bmp = wx.ArtProvider.GetBitmap(wx.ART_ERROR,
83                                                    wx.ART_TOOLBAR)
84            if icon_type == "info":
85                icon_bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION,
86                                                    wx.ART_TOOLBAR)
87        self.msg_txt.Newline()
88        self.msg_txt.WriteBitmap(icon_bmp)
89        self.msg_txt.BeginTextColour(color)
90        self.msg_txt.WriteText("\t")
91        self.msg_txt.AppendText(status)
92        self.msg_txt.EndTextColour()
93
94
95class Console(wx.Frame):
96    """
97    The main class defining the Console window.
98    """
99    def __init__(self, parent=None, status="", *args, **kwds):
100        kwds["size"] = (CONSOLE_WIDTH, CONSOLE_HEIGHT)
101        kwds["title"] = "Console"
102        wx.Frame.__init__(self, parent=parent, *args, **kwds)
103        self.SetWindowVariant(FONT_VARIANT)
104        self.panel = ConsolePanel(self)
105        self.panel.set_message(status=status)
106        wx.EVT_CLOSE(self, self.Close)
107
108    def set_multiple_messages(self, messages=[]):
109        """
110        Method to send an arbitrary number of messages to the console log
111
112        :param messages: A list of strings to be sent to the console log.
113        """
114        if messages:
115            for status in messages:
116                self.panel.set_message(status=status)
117
118    def set_message(self, status, event=None):
119        """
120        Exposing the base ConsolePanel set_message
121
122        :param status: A status message to be sent to the console log.
123        :param event: A wx event.
124        """
125        self.panel.set_message(status=str(status), event=event)
126
127    def Close(self, event):
128        """
129        Calling close on the panel will hide the panel.
130
131        :param event: A wx event.
132        """
133        self.Hide()
134
135class StatusBar(wxStatusB):
136    """
137        Application status bar
138    """
139    def __init__(self, parent, id):
140        wxStatusB.__init__(self, parent, id)
141        self.parent = parent
142        self.parent.SetStatusBarPane(MSG_POSITION)
143
144        #Layout of status bar
145        width = STATUS_BAR_ICON_SIZE
146        height = STATUS_BAR_ICON_SIZE
147        self.SetFieldsCount(NB_FIELDS)
148        # Leave some space for the resize handle in the last field
149        console_btn_width = 80
150        self.SetStatusWidths([width+4, -2, -1, width+console_btn_width])
151        self.SetMinHeight(height + 10)
152
153        #display default message
154        self.msg_position = MSG_POSITION
155
156        # Create progress bar
157        gauge_width = 5 * width
158        self.gauge = wx.Gauge(self, size=(gauge_width, height),
159                              style=wx.GA_HORIZONTAL)
160        self.gauge.Hide()
161
162        # Create status bar icon reflecting the type of status
163        # for the last message
164        self.status_color = wx.StaticText(self, id=wx.NewId(), label="   ",
165                                          size=wx.Size(15, 15))
166        self.status_color.SetBackgroundColour(GREEN)
167        self.status_color.SetForegroundColour(GREEN)
168
169        # Create the button used to show the console dialog
170        self.console_button = wx.Button(self, wx.NewId(), "Console",
171                                        size=(console_btn_width, -1))
172        font = self.console_button.GetFont()
173        _, pixel_h = font.GetPixelSize()
174        font.SetPixelSize(wx.Size(0, int(pixel_h*0.9)))
175        self.console_button.SetFont(font)
176        self.console_button.SetToolTipString("History of status bar messages")
177        self.console_button.Bind(wx.EVT_BUTTON, self._onMonitor,
178                                 id=self.console_button.GetId())
179
180        self.reposition()
181        ## Current progress value of the bar
182        self.nb_start = 0
183        self.nb_progress = 0
184        self.nb_stop = 0
185        self.frame = None
186        self.list_msg = []
187        self.frame = Console(parent=self)
188        if hasattr(self.frame, "IsIconized"):
189            if not self.frame.IsIconized():
190                try:
191                    icon = self.parent.GetIcon()
192                    self.frame.SetIcon(icon)
193                except:
194                    try:
195                        FRAME_ICON = wx.Icon(GUIFRAME_ICON.FRAME_ICON_PATH,
196                                             wx.BITMAP_TYPE_ICO)
197                        self.frame.SetIcon(FRAME_ICON)
198                    except:
199                        pass
200        self.frame.set_multiple_messages(self.list_msg)
201        self.frame.Hide()
202        self.progress = 0
203        self.timer = wx.Timer(self, -1)
204        self.timer_stop = wx.Timer(self, -1)
205        self.thread = None
206        self.Bind(wx.EVT_TIMER, self._on_time, self.timer)
207        self.Bind(wx.EVT_TIMER, self._on_time_stop, self.timer_stop)
208        self.Bind(wx.EVT_SIZE, self.on_size)
209        self.Bind(wx.EVT_IDLE, self.on_idle)
210
211    def reposition(self):
212        """
213        Place the various fields in their proper position
214        """
215        rect = self.GetFieldRect(GAUGE_POSITION)
216        self.gauge.SetPosition((rect.x, rect.y))
217        rect = self.GetFieldRect(ICON_POSITION)
218        self.status_color.SetPosition((rect.x, rect.y))
219        rect = self.GetFieldRect(CONSOLE_POSITION)
220        self.console_button.SetPosition((rect.x, rect.y))
221        self.size_changed = False
222
223    def on_idle(self, event):
224        """
225        When the window is idle, check if the window has been resized
226        """
227        if self.size_changed:
228            self.reposition()
229
230    def on_size(self, evt):
231        """
232        If the window is resized, redraw the window.
233        """
234        self.reposition() 
235        self.size_changed = True
236
237    def get_msg_position(self):
238        """
239        Get the last known message that was displayed on the console window.
240        """
241        return self.msg_position
242
243    def SetStatusText(self, text="", number=MSG_POSITION, event=None):
244        """
245        Set the text that will be displayed in the status bar.
246        """
247        wxStatusB.SetStatusText(self, text.split('\n', 1)[0], number)
248        self.list_msg.append(text)
249        self.status_color.SetBackgroundColour(GREEN)
250        self.status_color.SetForegroundColour(GREEN)
251
252        if self.frame is not None:
253            self.frame.set_message(status=text, event=event)
254
255    def PopStatusText(self, *args, **kwds):
256        """
257        Override status bar
258        """
259        wxStatusB.PopStatusText(self, field=MSG_POSITION)
260
261    def PushStatusText(self, *args, **kwds):
262        """
263        PushStatusText
264        """
265        text = "PushStatusText: What is this string?"
266        wxStatusB.PushStatusText(self, field=MSG_POSITION, string=text)
267
268    def enable_clear_gauge(self):
269        """
270        clear the progress bar
271        """
272        flag = True
273        # Why we do this?
274        #if (self.nb_start <= self.nb_stop) or \
275        #    (self.nb_progress <= self.nb_stop):
276        #    flag = True
277        return flag
278
279    def _on_time_stop(self, evt):
280        """
281        Clear the progress bar
282
283        :param evt: wx.EVT_TIMER
284        """
285        count = 0
286        while count <= 100:
287            count += 1
288        self.timer_stop.Stop()
289        self.clear_gauge(msg="")
290        self.nb_progress = 0
291        self.nb_start = 0
292        self.nb_stop = 0
293
294    def _on_time(self, evt):
295        """
296        Update the progress bar while the timer is running
297
298        :param evt: wx.EVT_TIMER
299        """
300        # Check stop flag that can be set from non main thread
301        if self.timer.IsRunning():
302            self.gauge.Pulse()
303
304    def clear_gauge(self, msg=""):
305        """
306        Hide the gauge
307        """
308        self.progress = 0
309        self.gauge.SetValue(0)
310        self.gauge.Hide()
311
312    def set_icon(self, event):
313        """
314        Display icons related to the type of message sent to the statusbar
315        when available. No icon is displayed if the message is empty
316        """
317        if hasattr(event, "status"):
318            status = str(event.status)
319            if status.strip() == "":
320                return
321        else:
322            return
323        if not hasattr(event, "info"):
324            return
325
326        # Get the size of the button images
327        height = STATUS_BAR_ICON_SIZE
328
329        msg = event.info.lower()
330        if msg == "warning":
331            self.status_color.SetBackgroundColour(YELLOW)
332            self.status_color.SetForegroundColour(YELLOW)
333        elif msg == "error":
334            self.status_color.SetBackgroundColour(RED)
335            self.status_color.SetForegroundColour(RED)
336        else:
337            self.status_color.SetBackgroundColour(GREEN)
338            self.status_color.SetForegroundColour(GREEN)
339
340    def set_dialog(self, event):
341        """
342        Display dialogbox
343        """
344        if not hasattr(event, "info"):
345            return
346        msg = event.info.lower()
347        if msg == "error":
348            e_msg = "Error(s) Occurred:\n"
349            e_msg += "\t" + event.status + "\n\n"
350            e_msg += "Further information might be available in "
351            e_msg += "the Console log (bottom right corner)."
352            wx.MessageBox(e_msg, style=wx.ICON_ERROR)
353
354    def set_message(self, event):
355        """
356        display received message on the statusbar
357        """
358        if hasattr(event, "status"):
359            self.SetStatusText(text=str(event.status), event=event)
360 
361    def set_gauge(self, event):
362        """
363        change the state of the gauge according the state of the current job
364        """
365        if not hasattr(event, "type"):
366            return
367        type = event.type
368        self.gauge.Show(True)
369        if type.lower() == "start":
370            self.nb_start += 1
371            #self.timer.Stop()
372            self.progress += 5
373            self.gauge.SetValue(int(self.progress))
374            self.progress += 5
375            if self.progress < self.gauge.GetRange() - 20:
376                self.gauge.SetValue(int(self.progress))
377        if type.lower() == "progress":
378            self.nb_progress += 1
379            self.timer.Start(1)
380            self.gauge.Pulse()
381        if type.lower() == "update":
382            self.progress += 5
383            if self.progress < self.gauge.GetRange()- 20:
384                self.gauge.SetValue(int(self.progress))
385        if type.lower() == "stop":
386            self.nb_stop += 1
387            self.gauge.Show(True)
388            if self.enable_clear_gauge():
389                self.timer.Stop()
390                self.progress = 0
391                self.gauge.SetValue(100)
392                self.timer_stop.Start(5)
393
394    def set_status(self, event):
395        """
396        Update the status bar .
397
398        :param type: type of message send.
399            type  must be in ["start","progress","update","stop"]
400        :param msg: the message itself  as string
401        :param thread: if updatting using a thread status
402
403        """
404        self.set_message(event=event)
405        self.set_icon(event=event)
406        self.set_gauge(event=event)
407        # dialog on error
408        self.set_dialog(event=event)
409
410    def _onMonitor(self, event):
411        """
412        Pop up a frame with messages sent to the status bar
413        """
414        self.frame.Show(False)
415        self.frame.Show(True)
416
417
418class SPageStatusbar(wxStatusB):
419    def __init__(self, parent, timeout=None, *args, **kwds):
420        wxStatusB.__init__(self, parent, *args, **kwds)
421        self.SetFieldsCount(1)
422        self.timeout = timeout
423        width, height = parent.GetSizeTuple()
424        self.gauge = wx.Gauge(self, style=wx.GA_HORIZONTAL,
425                              size=(width, height/10))
426        rect = self.GetFieldRect(0)
427        self.gauge.SetPosition((rect.x , rect.y ))
428        if self.timeout is not None:
429            self.gauge.SetRange(int(self.timeout))
430        self.timer = wx.Timer(self, -1)
431        self.Bind(wx.EVT_TIMER, self._on_time, self.timer)
432        self.timer.Start(1)
433        self.pos = 0
434
435    def _on_time(self, evt):
436        """
437        Update the progress bar while the timer is running
438
439        :param evt: wx.EVT_TIMER
440
441        """
442        # Check stop flag that can be set from non main thread
443        if self.timeout is None and self.timer.IsRunning():
444            self.gauge.Pulse()
445
446
447if __name__ == "__main__":
448    app = wx.PySimpleApp()
449    frame = wx.Frame(None, wx.ID_ANY, 'test frame')
450    #statusBar = StatusBar(frame, wx.ID_ANY)
451    statusBar = SPageStatusbar(frame)
452    frame.SetStatusBar(statusBar)
453    frame.Show(True)
454    #event = MessageEvent()
455    #event.type = "progress"
456    #event.status  = "statusbar...."
457    #event.info = "error"
458    #statusBar.set_status(event=event)
459    app.MainLoop()
460
Note: See TracBrowser for help on using the repository browser.