Poodletooth-iLand/panda/python/Lib/site-packages/wx/lib/agw/balloontip.py
2015-03-06 06:11:40 -06:00

1157 lines
37 KiB
Python

# --------------------------------------------------------------------------- #
# BALLOONTIP wxPython IMPLEMENTATION
# Python Code By:
#
# Andrea Gavana, @ 29 May 2005
# Latest Revision: 16 Jul 2012, 15.00 GMT
#
#
# TODO List/Caveats
#
# 1. With wx.ListBox (And Probably Other Controls), The BalloonTip Sometimes
# Flashes (It Is Created And Suddenly Destroyed). I Don't Know What Is
# Happening. Probably I Don't Handle Correctly The wx.EVT_ENTER_WINDOW
# wx.EVT_LEAVE_WINDOW?
#
# 2. wx.RadioBox Seems Not To Receive The wx.EVT_ENTER_WINDOW Event
#
# 3. wx.SpinCtrl (And Probably Other Controls), When Put In A Sizer, Does Not
# Return The Correct Size/Position. Probably Is Something I Am Missing.
#
# 4. Other Issues?
#
#
# FIXED Problems
#
# 1. Now BalloonTip Control Works Also For TaskBarIcon (Thanks To Everyone
# For The Suggetions I Read In The wxPython Mailing List)
#
#
# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
# Write To Me At:
#
# andrea.gavana@gmail.com
# andrea.gavana@maerskoil.com
#
# Or, Obviously, To The wxPython Mailing List!!!
#
# Tags: phoenix-port, unittest, documented
#
# End Of Comments
# --------------------------------------------------------------------------- #
"""
:class:`BalloonTip` is a class that allows you to display tooltips in a balloon style
window.
Description
===========
:class:`BalloonTip` is a class that allows you to display tooltips in a balloon style
window (actually a frame), similarly to the windows XP balloon help. There is
also an arrow that points to the center of the control designed as a "target"
for the :class:`BalloonTip`.
What it can do:
- Set the balloon shape as a rectangle or a rounded rectangle;
- Set an icon to the top-left of the :class:`BalloonTip` frame;
- Set a title at the top of the :class:`BalloonTip` frame;
- Automatic "best" placement of :class:`BalloonTip` frame depending on the target
control/window position;
- Runtime customization of title/tip fonts and foreground colours;
- Runtime change of :class:`BalloonTip` frame shape;
- Set the balloon background colour;
- Possibility to set the delay after which the :class:`BalloonTip` is displayed;
- Possibility to set the delay after which the :class:`BalloonTip` is destroyed;
- Three different behaviors for the :class:`BalloonTip` window (regardless the delay
destruction time set):
a) Destroy by leave: the :class:`BalloonTip` is destroyed when the mouse leaves the
target control/window;
b) Destroy by click: the :class:`BalloonTip` is destroyed when you click on any area
of the target control/window;
c) Destroy by button: the :class:`BalloonTip` is destroyed when you click on the
top-right close button;
- Possibility to enable/disable globally the :class:`BalloonTip` on you application;
- Set the :class:`BalloonTip` also for the taskbar icon.
Usage
=====
Usage example::
import wx
import wx.lib.agw.balloontip as BT
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "BalloonTip Demo")
panel = wx.Panel(self)
# Let's suppose that in your application you have a wx.TextCtrl defined as:
mytextctrl = wx.TextCtrl(panel, -1, "I am a textctrl", pos=(100, 100))
# You can define your BalloonTip as follows:
tipballoon = BT.BalloonTip(topicon=None, toptitle="textctrl",
message="this is a textctrl",
shape=BT.BT_ROUNDED,
tipstyle=BT.BT_LEAVE)
# Set the BalloonTip target
tipballoon.SetTarget(mytextctrl)
# Set the BalloonTip background colour
tipballoon.SetBalloonColour(wx.WHITE)
# Set the font for the balloon title
tipballoon.SetTitleFont(wx.Font(9, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False))
# Set the colour for the balloon title
tipballoon.SetTitleColour(wx.BLACK)
# Leave the message font as default
tipballoon.SetMessageFont()
# Set the message (tip) foreground colour
tipballoon.SetMessageColour(wx.LIGHT_GREY)
# Set the start delay for the BalloonTip
tipballoon.SetStartDelay(1000)
# Set the time after which the BalloonTip is destroyed
tipballoon.SetEndDelay(3000)
# our normal wxApp-derived class, as usual
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
Window Styles
=============
This class supports the following window styles:
================ =========== ==================================================
Window Styles Hex Value Description
================ =========== ==================================================
``BT_ROUNDED`` 0x1 :class:`BalloonTip` will have a rounded rectangular shape.
``BT_RECTANGLE`` 0x2 :class:`BalloonTip` will have a rectangular shape.
``BT_LEAVE`` 0x3 :class:`BalloonTip` will be destroyed when the user moves the mouse outside the target window.
``BT_CLICK`` 0x4 :class:`BalloonTip` will be destroyed when the user click on :class:`BalloonTip`.
``BT_BUTTON`` 0x5 :class:`BalloonTip` will be destroyed when the user click on the close button.
================ =========== ==================================================
Events Processing
=================
`No custom events are available for this class.`
License And Version
===================
BalloonTip is distributed under the wxPython license.
Latest revision: Andrea Gavana @ 16 Jul 2012, 15.00 GMT
Version 0.3
"""
import wx
import time
import wx.adv
from wx.lib.buttons import GenButton
# Define The Values For The BalloonTip Frame Shape
BT_ROUNDED = 1
""" :class:`BalloonTip` will have a rounded rectangular shape. """
BT_RECTANGLE = 2
""" :class:`BalloonTip` will have a rectangular shape. """
# Define The Value For The BalloonTip Destruction Behavior
BT_LEAVE = 3
""" :class:`BalloonTip` will be destroyed when the user moves the mouse outside the target window. """
BT_CLICK = 4
""" :class:`BalloonTip` will be destroyed when the user click on :class:`BalloonTip`. """
BT_BUTTON = 5
""" :class:`BalloonTip` will be destroyed when the user click on the close button. """
# ---------------------------------------------------------------
# Class BalloonFrame
# ---------------------------------------------------------------
# This Class Is Called By The Main BalloonTip Class, And It Is
# Responsible For The Frame Creation/Positioning On Screen
# Depending On Target Control/Window, The Frame Can Position
# Itself To NW (Default), NE, SW, SE. The Switch On Positioning
# Is Done By Calculating The Absolute Position Of The Target
# Control/Window Plus/Minus The BalloonTip Size. The Pointing
# Arrow Is Positioned Accordingly.
# ---------------------------------------------------------------
class BalloonFrame(wx.Frame):
"""
This class is called by the main :class:`BalloonTip` class, and it is
responsible for the frame creation/positioning on screen
depending on target control/window, the frame can position
itself to NW (default), NE, SW, SE. The switch on positioning
is done by calculating the absolute position of the target
control/window plus/minus the balloontip size. The pointing
arrow is positioned accordingly.
"""
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
size=wx.DefaultSize, classparent=None):
"""
Default class constructor.
Used internally. Do not call directly this class in your application!
"""
wx.Frame.__init__(self, None, -1, "BalloonTip", pos, size,
style=wx.FRAME_SHAPED |
wx.SIMPLE_BORDER |
wx.FRAME_NO_TASKBAR |
wx.STAY_ON_TOP)
self._parent = classparent
self._toptitle = self._parent._toptitle
self._topicon = self._parent._topicon
self._message = self._parent._message
self._shape = self._parent._shape
self._tipstyle = self._parent._tipstyle
self._ballooncolour = self._parent._ballooncolour
self._balloonmsgcolour = self._parent._balloonmsgcolour
self._balloonmsgfont = self._parent._balloonmsgfont
if self._toptitle != "":
self._balloontitlecolour = self._parent._balloontitlecolour
self._balloontitlefont = self._parent._balloontitlefont
panel = wx.Panel(self, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
self.panel = panel
subsizer = wx.BoxSizer(wx.VERTICAL)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
subsizer.Add((0,20), 0, wx.EXPAND)
if self._topicon is not None:
stb = wx.StaticBitmap(panel, -1, self._topicon)
hsizer.Add(stb, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 10)
self._balloonbmp = stb
if self._toptitle != "":
stt = wx.StaticText(panel, -1, self._toptitle)
stt.SetFont(wx.Font(9, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False))
if self._topicon is None:
hsizer.Add((10,0), 0, wx.EXPAND)
hsizer.Add(stt, 1, wx.EXPAND | wx.TOP, 10)
self._balloontitle = stt
self._balloontitle.SetForegroundColour(self._balloontitlecolour)
self._balloontitle.SetFont(self._balloontitlefont)
if self._tipstyle == BT_BUTTON:
self._closebutton = GenButton(panel, -1, "X", style=wx.NO_BORDER)
self._closebutton.SetMinSize((16,16))
self._closebutton.SetFont(wx.Font(9, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False))
self._closebutton.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterButton)
self._closebutton.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveButton)
self._closebutton.SetUseFocusIndicator(False)
if self._toptitle != "":
hsizer.Add(self._closebutton, 0, wx.TOP | wx.RIGHT, 5)
else:
hsizer.Add((10,0), 1, wx.EXPAND)
hsizer.Add(self._closebutton, 0, wx.ALIGN_RIGHT | wx.TOP
| wx.RIGHT, 5)
if self._topicon is not None or self._toptitle != "" \
or self._tipstyle == BT_BUTTON:
subsizer.Add(hsizer, 0, wx.EXPAND | wx.BOTTOM, 5)
self._firstline = line = wx.StaticLine(panel, -1, style=wx.LI_HORIZONTAL)
if self._topicon is not None or self._toptitle != "" \
or self._tipstyle == BT_BUTTON:
subsizer.Add(self._firstline, 0, wx.EXPAND | wx.LEFT | wx.RIGHT
| wx.BOTTOM, 10)
else:
subsizer.Add(self._firstline, 0, wx.EXPAND | wx.LEFT | wx.RIGHT
| wx.BOTTOM | wx.TOP, 10)
mainstt = wx.StaticText(panel, -1, self._message)
self._balloonmsg = mainstt
self._balloonmsg.SetForegroundColour(self._balloonmsgcolour)
self._balloonmsg.SetFont(self._balloonmsgfont)
subsizer.Add(self._balloonmsg, 1, wx.EXPAND | wx.LEFT | wx.RIGHT |
wx.BOTTOM, 10)
self._secondline = wx.StaticLine(panel, -1, style=wx.LI_HORIZONTAL)
subsizer.Add(self._secondline, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 10)
subsizer.Add((0,0),1)
panel.SetSizer(subsizer)
sizer.Add(panel, 1, wx.EXPAND)
self.SetSizerAndFit(sizer)
sizer.Layout()
if self._tipstyle == BT_CLICK:
if self._toptitle != "":
self._balloontitle.Bind(wx.EVT_LEFT_DOWN, self.OnClose)
if self._topicon is not None:
self._balloonbmp.Bind(wx.EVT_LEFT_DOWN, self.OnClose)
self._balloonmsg.Bind(wx.EVT_LEFT_DOWN, self.OnClose)
self.panel.Bind(wx.EVT_LEFT_DOWN, self.OnClose)
elif self._tipstyle == BT_BUTTON:
self._closebutton.Bind(wx.EVT_BUTTON, self.OnClose)
self.panel.SetBackgroundColour(self._ballooncolour)
if wx.Platform == "__WXGTK__":
self.Bind(wx.EVT_WINDOW_CREATE, self.SetBalloonShape)
else:
self.SetBalloonShape()
self.Show(True)
def SetBalloonShape(self, event=None):
"""
Sets the balloon shape.
:param `event`: on wxGTK, a :class:`WindowCreateEvent` event to process.
"""
size = self.GetSize()
pos = self.GetPosition()
dc = wx.MemoryDC(wx.Bitmap(1,1))
textlabel = self._balloonmsg.GetLabel()
textfont = self._balloonmsg.GetFont()
textextent = dc.GetFullTextExtent(textlabel, textfont)
boxheight = size.y - textextent[1]*len(textlabel.split("\n"))
boxwidth = size.x
position = wx.GetMousePosition()
xpos = position[0]
ypos = position[1]
if xpos > 20 and ypos > 20:
# This Is NW Positioning
positioning = "NW"
xpos = position[0] - boxwidth + 20
ypos = position[1] - boxheight - 20
elif xpos <= 20 and ypos <= 20:
# This Is SE Positioning
positioning = "SE"
xpos = position[0] - 20
ypos = position[1]
elif xpos > 20 and ypos <= 20:
# This Is SW Positioning
positioning = "SW"
xpos = position[0] - boxwidth + 20
ypos = position[1]
else:
# This Is NE Positioning
positioning = "NE"
xpos = position[0]
ypos = position[1] - boxheight + 20
bmp = wx.Bitmap(size.x,size.y)
dc = wx.BufferedDC(None, bmp)
dc.SetBackground(wx.Brush(wx.Colour(0,0,0)))
dc.Clear()
dc.SetPen(wx.Pen(wx.Colour(0,0,0), 1, wx.PENSTYLE_TRANSPARENT))
if self._shape == BT_ROUNDED:
dc.DrawRoundedRectangle(0, 20, boxwidth, boxheight-20, 12)
elif self._shape == BT_RECTANGLE:
dc.DrawRectangle(0, 20, boxwidth, boxheight-20)
if positioning == "NW":
dc.DrawPolygon(((boxwidth-40, boxheight), (boxwidth-20, boxheight+20),
(boxwidth-20, boxheight)))
elif positioning == "SE":
dc.DrawPolygon(((20, 20), (20, 0), (40, 20)))
elif positioning == "SW":
dc.DrawPolygon(((boxwidth-40, 20), (boxwidth-20, 0), (boxwidth-20, 20)))
else:
dc.DrawPolygon(((20, boxheight), (20, boxheight+20), (40, boxheight)))
r = wx.Region(bmp, wx.Colour(0, 0, 0))
self.hasShape = self.SetShape(r)
if self._tipstyle == BT_BUTTON:
colour = self.panel.GetBackgroundColour()
self._closebutton.SetBackgroundColour(colour)
self.SetPosition((xpos, ypos))
def OnEnterButton(self, event):
"""
Handles the ``wx.EVT_ENTER_WINDOW`` for the :class:`BalloonTip` button.
When the :class:`BalloonTip` is created with the `tipstyle` = ``BT_BUTTON``, this event
provide some kind of 3D effect when the mouse enters the button area.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
button = event.GetEventObject()
colour = button.GetBackgroundColour()
red = colour.Red()
green = colour.Green()
blue = colour.Blue()
if red < 30:
red = red + 30
if green < 30:
green = green + 30
if blue < 30:
blue = blue + 30
colour = wx.Colour(red-30, green-30, blue-30)
button.SetBackgroundColour(colour)
button.SetForegroundColour(wx.WHITE)
button.Refresh()
event.Skip()
def OnLeaveButton(self, event):
"""
Handles the ``wx.EVT_LEAVE_WINDOW`` for the :class:`BalloonTip` button.
When the :class:`BalloonTip` is created with the `tipstyle` = ``BT_BUTTON``, this event
provide some kind of 3D effect when the mouse enters the button area.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
button = event.GetEventObject()
colour = self.panel.GetBackgroundColour()
button.SetBackgroundColour(colour)
button.SetForegroundColour(wx.BLACK)
button.Refresh()
event.Skip()
def OnClose(self, event):
"""
Handles the ``wx.EVT_CLOSE`` event for :class:`BalloonTip`.
:param `event`: a :class:`CloseEvent` event to be processed.
"""
if isinstance(self._parent._widget, wx.adv.TaskBarIcon):
self._parent.taskbarcreation = 0
self._parent.taskbartime.Stop()
del self._parent.taskbartime
del self._parent.BalloonFrame
self.Destroy()
# ---------------------------------------------------------------
# Class BalloonTip
# ---------------------------------------------------------------
# This Is The Main BalloonTip Implementation
# ---------------------------------------------------------------
class BalloonTip(object):
"""
:class:`BalloonTip` is a class that allows you to display tooltips in a balloon style
window.
This is the main class implementation.
"""
def __init__(self, topicon=None, toptitle="",
message="", shape=BT_ROUNDED, tipstyle=BT_LEAVE):
"""
Default class constructor.
:param `topicon`: an icon that will be displayed on the top-left part of the
:class:`BalloonTip` frame. If set to ``None``, no icon will be displayed;
:type `topicon`: :class:`Bitmap` or ``None``
:param string `toptitle`: a title that will be displayed on the top part of the
:class:`BalloonTip` frame. If set to an empty string, no title will be displayed;
:param string `message`: the tip message that will be displayed. It can not be set to
an empty string;
:param integer `shape`: the :class:`BalloonTip` shape. It can be one of the following:
======================= ========= ====================================
Shape Flag Hex Value Description
======================= ========= ====================================
``BT_ROUNDED`` 0x1 :class:`BalloonTip` will have a rounded rectangular shape.
``BT_RECTANGLE`` 0x2 :class:`BalloonTip` will have a rectangular shape.
======================= ========= ====================================
:param integer `tipstyle`: the :class:`BalloonTip` destruction behavior. It can be one of:
======================= ========= ====================================
Tip Flag Hex Value Description
======================= ========= ====================================
``BT_LEAVE`` 0x3 :class:`BalloonTip` will be destroyed when the user moves the mouse outside the target window.
``BT_CLICK`` 0x4 :class:`BalloonTip` will be destroyed when the user click on :class:`BalloonTip`.
``BT_BUTTON`` 0x5 :class:`BalloonTip` will be destroyed when the user click on the close button.
======================= ========= ====================================
:raise: `Exception` in the following cases:
- The `message` parameter is an empty string;
- The `shape` parameter has an invalid value (i.e., it's not one of ``BT_ROUNDED``, ``BT_RECTANGLE``);
- The `tipstyle` parameter has an invalid value (i.e., it's not one of ``BT_LEAVE``, ``BT_CLICK``, ``BT_BUTTON``).
"""
self._shape = shape
self._topicon = topicon
self._toptitle = toptitle
self._message = message
self._tipstyle = tipstyle
app = wx.GetApp()
self._runningapp = app
self._runningapp.__tooltipenabled__ = True
if self._message == "":
raise Exception("\nERROR: You Should At Least Set The Message For The BalloonTip")
if self._shape not in [BT_ROUNDED, BT_RECTANGLE]:
raise Exception('\nERROR: BalloonTip Shape Should Be One Of "BT_ROUNDED", "BT_RECTANGLE"')
if self._tipstyle not in [BT_LEAVE, BT_CLICK, BT_BUTTON]:
raise Exception('\nERROR: BalloonTip TipStyle Should Be One Of "BT_LEAVE", '\
'"BT_CLICK", "BT_BUTTON"')
self.SetStartDelay()
self.SetEndDelay()
self.SetBalloonColour()
if toptitle != "":
self.SetTitleFont()
self.SetTitleColour()
if topicon is not None:
self.SetBalloonIcon(topicon)
self.SetMessageFont()
self.SetMessageColour()
def SetTarget(self, widget):
"""
Sets the target control/window for the :class:`BalloonTip`.
:param `widget`: any subclass of :class:`Window`.
"""
self._widget = widget
if isinstance(widget, wx.adv.TaskBarIcon):
self._widget.Bind(wx.adv.EVT_TASKBAR_MOVE, self.OnTaskBarMove)
self._widget.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
self.taskbarcreation = 0
else:
self._widget.Bind(wx.EVT_ENTER_WINDOW, self.OnWidgetEnter)
self._widget.Bind(wx.EVT_LEAVE_WINDOW, self.OnWidgetLeave)
self._widget.Bind(wx.EVT_MOTION, self.OnWidgetMotion)
self._widget.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
def GetTarget(self):
"""
Returns the target window for the :class:`BalloonTip`.
:return: An instance of :class:`Window`.
:raise: `Exception` if the :meth:`~BalloonTip.SetTarget` method has not previously called.
"""
if not hasattr(self, "_widget"):
raise Exception("\nERROR: BalloonTip Target Has Not Been Set")
return self._widget
def SetStartDelay(self, delay=1):
"""
Sets the delay time after which the :class:`BalloonTip` is created.
:param integer `delay`: the number of milliseconds after which :class:`BalloonTip` is created.
:raise: `Exception` if `delay` is less than ``1`` milliseconds.
"""
if delay < 1:
raise Exception("\nERROR: Delay Time For BalloonTip Creation Should Be Greater Than 1 ms")
self._startdelaytime = float(delay)
def GetStartDelay(self):
"""
Returns the delay time after which the :class:`BalloonTip` is created.
:return: the delay time, in milliseconds.
"""
return self._startdelaytime
def SetEndDelay(self, delay=1e6):
"""
Sets the delay time after which the BalloonTip is destroyed.
:param integer `delay`: the number of milliseconds after which :class:`BalloonTip` is destroyed.
:raise: `Exception` if `delay` is less than ``1`` milliseconds.
"""
if delay < 1:
raise Exception("\nERROR: Delay Time For BalloonTip Destruction Should Be Greater Than 1 ms")
self._enddelaytime = float(delay)
def GetEndDelay(self):
"""
Returns the delay time after which the :class:`BalloonTip` is destroyed.
:return: the delay time, in milliseconds.
"""
return self._enddelaytime
def OnWidgetEnter(self, event):
"""
Handles the ``wx.EVT_ENTER_WINDOW`` for the target control/window and
starts the :class:`BalloonTip` timer for creation.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if hasattr(self, "BalloonFrame"):
if self.BalloonFrame:
return
if not self._runningapp.__tooltipenabled__:
return
self.showtime = wx.Timer(self._widget)
self._widget.Bind(wx.EVT_TIMER, self.NotifyTimer, self.showtime)
self.showtime.Start(self._startdelaytime)
event.Skip()
def OnWidgetLeave(self, event):
"""
Handles the ``wx.EVT_LEAVE_WINDOW`` for the target control/window.
:param `event`: a :class:`MouseEvent` event to be processed.
:note: If the BalloonTip `tipstyle` is set to ``BT_LEAVE``, the :class:`BalloonTip` is destroyed.
"""
if hasattr(self, "showtime"):
if self.showtime:
self.showtime.Stop()
del self.showtime
if hasattr(self, "BalloonFrame"):
if self.BalloonFrame:
if self._tipstyle == BT_LEAVE:
endtime = time.time()
if endtime - self.starttime > 0.1:
try:
self.BalloonFrame.Destroy()
except:
pass
else:
event.Skip()
else:
event.Skip()
else:
event.Skip()
def OnTaskBarMove(self, event):
"""
Handles the mouse motion inside the taskbar icon area.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if not hasattr(self, "BalloonFrame"):
if self.taskbarcreation == 0:
self.mousepos = wx.GetMousePosition()
self.currentmousepos = self.mousepos
self.taskbartime = wx.Timer(self._widget)
self._widget.Bind(wx.EVT_TIMER, self.TaskBarTimer, self.taskbartime)
self.taskbartime.Start(100)
self.showtime = wx.Timer(self._widget)
self._widget.Bind(wx.EVT_TIMER, self.NotifyTimer, self.showtime)
self.showtime.Start(self._startdelaytime)
if self.taskbarcreation == 0:
self.taskbarcreation = 1
return
event.Skip()
def OnWidgetMotion(self, event):
"""
Handle the mouse motion inside the target.
This prevents the annoying behavior of :class:`BalloonTip` to display when the
user does something else inside the window. The :class:`BalloonTip` window is
displayed only when the mouse does *not* move for the start delay time.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if hasattr(self, "BalloonFrame"):
if self.BalloonFrame:
return
if hasattr(self, "showtime"):
if self.showtime:
self.showtime.Start(self._startdelaytime)
event.Skip()
def NotifyTimer(self, event):
"""
The creation timer has expired. Creates the :class:`BalloonTip` frame.
:param `event`: a :class:`wx.TimerEvent` to be processed.
"""
self.BalloonFrame = BalloonFrame(self._widget, classparent=self)
self.BalloonFrame.Show(True)
self.starttime = time.time()
if hasattr(self, "showtime"):
self.showtime.Stop()
del self.showtime
self.destroytime = wx.Timer(self._widget)
self._widget.Bind(wx.EVT_TIMER, self.NotifyTimer, self.destroytime)
self.destroytime.Start(self._enddelaytime)
def TaskBarTimer(self, event):
"""
This timer check periodically the mouse position.
If the current mouse position is sufficiently far from the coordinates
it had when entered the taskbar icon and the :class:`BalloonTip` style is
``BT_LEAVE``, the :class:`BalloonTip` frame is destroyed.
:param `event`: a :class:`wx.TimerEvent` to be processed.
"""
self.currentmousepos = wx.GetMousePosition()
mousepos = self.mousepos
if abs(self.currentmousepos[0] - mousepos[0]) > 30 or \
abs(self.currentmousepos[1] - mousepos[1]) > 30:
if hasattr(self, "BalloonFrame"):
if self._tipstyle == BT_LEAVE:
try:
self.BalloonFrame.Destroy()
self.taskbartime.Stop()
del self.taskbartime
del self.BalloonFrame
self.taskbarcreation = 0
except:
pass
def DestroyTimer(self, event):
"""
The destruction timer has expired. Destroys the :class:`BalloonTip` frame.
:param `event`: a :class:`wx.TimerEvent` to be processed.
"""
self.destroytime.Stop()
del self.destroytime
try:
self.BalloonFrame.Destroy()
except:
pass
def SetBalloonShape(self, shape=BT_ROUNDED):
"""
Sets the :class:`BalloonTip` frame shape.
:param integer `shape`: should be one of ``BT_ROUNDED`` or ``BT_RECTANGLE``.
:raise: `Exception` if the `shape` parameter is not a valid value
(i.e., it's not one of ``BT_ROUNDED``, ``BT_RECTANGLE``);
"""
if shape not in [BT_ROUNDED, BT_RECTANGLE]:
raise Exception('\nERROR: BalloonTip Shape Should Be One Of "BT_ROUNDED", "BT_RECTANGLE"')
self._shape = shape
def GetBalloonShape(self):
"""
Returns the :class:`BalloonTip` frame shape.
:return: An integer, one of ``BT_ROUNDED``, ``BT_RECTANGLE``.
"""
return self._shape
def SetBalloonIcon(self, icon):
"""
Sets the :class:`BalloonTip` top-left icon.
:param `icon`: an instance of :class:`Bitmap`.
:raise: `Exception` if the `icon` bitmap is not a valid :class:`Bitmap`.
"""
if icon.IsOk():
self._topicon = icon
else:
raise Exception("\nERROR: Invalid Image Passed To BalloonTip")
def GetBalloonIcon(self):
"""
Returns the :class:`BalloonTip` top-left icon.
:return: An instance of :class:`Bitmap`.
"""
return self._topicon
def SetBalloonTitle(self, title=""):
"""
Sets the :class:`BalloonTip` top title.
:param string `title`: a string to use as a :class:`BalloonTip` title.
"""
self._toptitle = title
def GetBalloonTitle(self):
"""
Returns the :class:`BalloonTip` top title.
:return: A string containing the top title.
"""
return self._toptitle
def SetBalloonMessage(self, message):
"""
Sets the :class:`BalloonTip` tip message.
:param string `message`: a string identifying the main message body of :class:`BalloonTip`.
:raise: `Exception` if the message is an empty string.
:note: The :class:`BalloonTip` message should never be empty.
"""
if len(message.strip()) < 1:
raise Exception("\nERROR: BalloonTip Message Can Not Be Empty")
self._message = message
def GetBalloonMessage(self):
"""
Returns the :class:`BalloonTip` tip message.
:return: A string containing the main message.
"""
return self._message
def SetBalloonTipStyle(self, tipstyle=BT_LEAVE):
"""
Sets the :class:`BalloonTip` `tipstyle` parameter.
:param integer `tipstyle`: one of the following bit set:
============== ========== =====================================
Tip Style Hex Value Description
============== ========== =====================================
``BT_LEAVE`` 0x3 :class:`BalloonTip` will be destroyed when the user moves the mouse outside the target window.
``BT_CLICK`` 0x4 :class:`BalloonTip` will be destroyed when the user click on :class:`BalloonTip`.
``BT_BUTTON`` 0x5 :class:`BalloonTip` will be destroyed when the user click on the close button.
============== ========== =====================================
:raise: `Exception` if the `tipstyle` parameter has an invalid value
(i.e., it's not one of ``BT_LEAVE``, ``BT_CLICK``, ``BT_BUTTON``).
"""
if tipstyle not in [BT_LEAVE, BT_CLICK, BT_BUTTON]:
raise Exception('\nERROR: BalloonTip TipStyle Should Be One Of "BT_LEAVE", '\
'"BT_CLICK", "BT_BUTTON"')
self._tipstyle = tipstyle
def GetBalloonTipStyle(self):
"""
Returns the :class:`BalloonTip` `tipstyle` parameter.
:return: An integer representing the style.
:see: :meth:`~BalloonTip.SetBalloonTipStyle`
"""
return self._tipstyle
def SetBalloonColour(self, colour=None):
"""
Sets the :class:`BalloonTip` background colour.
:param `colour`: a valid :class:`Colour` instance.
"""
if colour is None:
colour = wx.Colour(255, 250, 205)
self._ballooncolour = colour
def GetBalloonColour(self):
"""
Returns the :class:`BalloonTip` background colour.
:return: An instance of :class:`Colour`.
"""
return self._ballooncolour
def SetTitleFont(self, font=None):
"""
Sets the font for the top title.
:param `font`: a valid :class:`Font` instance.
"""
if font is None:
font = wx.Font(9, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False)
self._balloontitlefont = font
def GetTitleFont(self):
"""
Returns the font for the top title.
:return: An instance of :class:`Font`.
"""
return self._balloontitlefont
def SetMessageFont(self, font=None):
"""
Sets the font for the tip message.
:param `font`: a valid :class:`Font` instance.
"""
if font is None:
font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
self._balloonmsgfont = font
def GetMessageFont(self):
"""
Returns the font for the tip message.
:return: An instance of :class:`Font`.
"""
return self._balloonmsgfont
def SetTitleColour(self, colour=None):
"""
Sets the colour for the top title.
:param `colour`: a valid :class:`Colour` instance.
"""
if colour is None:
colour = wx.BLACK
self._balloontitlecolour = colour
def GetTitleColour(self):
"""
Returns the colour for the top title.
:return: An instance of :class:`Colour`.
"""
return self._balloontitlecolour
def SetMessageColour(self, colour=None):
"""
Sets the colour for the tip message.
:param `colour`: a valid :class:`Colour` instance.
"""
if colour is None:
colour = wx.BLACK
self._balloonmsgcolour = colour
def GetMessageColour(self):
"""
Returns the colour for the tip message.
:return: An instance of :class:`Colour`.
"""
return self._balloonmsgcolour
def OnDestroy(self, event):
"""
Handles the target destruction, specifically handling the ``wx.EVT_WINDOW_DESTROY``
event.
:param `event`: a :class:`WindowDestroyEvent` event to be processed.
"""
if hasattr(self, "BalloonFrame"):
if self.BalloonFrame:
try:
if isinstance(self._widget, wx.adv.TaskBarIcon):
self._widget.Unbind(wx.adv.EVT_TASKBAR_MOVE)
self.taskbartime.Stop()
del self.taskbartime
else:
self._widget.Unbind(wx.EVT_MOTION)
self._widget.Unbind(wx.EVT_LEAVE_WINDOW)
self._widget.Unbind(wx.EVT_ENTER_WINDOW)
self.BalloonFrame.Destroy()
except:
pass
del self.BalloonFrame
def EnableTip(self, enable=True):
"""
Enable/disable globally the :class:`BalloonTip`.
:param bool `enable`: ``True`` to enable :class:`BalloonTip`, ``False`` otherwise.
"""
self._runningapp.__tooltipenabled__ = enable
if __name__ == '__main__':
import wx
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "BalloonTip Demo")
panel = wx.Panel(self)
# Let's suppose that in your application you have a wx.TextCtrl defined as:
mytextctrl = wx.TextCtrl(panel, -1, "I am a textctrl", pos=(100, 100))
# You can define your BalloonTip as follows:
tipballoon = BalloonTip(topicon=None, toptitle="textctrl",
message="this is a textctrl",
shape=BT_ROUNDED,
tipstyle=BT_LEAVE)
# Set the BalloonTip target
tipballoon.SetTarget(mytextctrl)
# Set the BalloonTip background colour
tipballoon.SetBalloonColour(wx.WHITE)
# Set the font for the balloon title
tipballoon.SetTitleFont(wx.Font(9, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False))
# Set the colour for the balloon title
tipballoon.SetTitleColour(wx.BLACK)
# Leave the message font as default
tipballoon.SetMessageFont()
# Set the message (tip) foreground colour
tipballoon.SetMessageColour(wx.LIGHT_GREY)
# Set the start delay for the BalloonTip
tipballoon.SetStartDelay(1000)
# Set the time after which the BalloonTip is destroyed
tipballoon.SetEndDelay(3000)
# our normal wxApp-derived class, as usual
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()