mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-28 14:12:52 -06:00
1157 lines
37 KiB
Python
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()
|
|
|
|
|