<EDITED> Python 2.7 tkinter (and IDLE) "problems" - python

I got some tkinter-based problems when I used the Python IDLE.
First, IDLE couldn't be opened in my computer
I use Python 2.7.12.
After openning IDLE through the windows command line, I've found where the problem is.
It's in Tkinter:
###C:\Python27\Lib\lib-tk\Tkinter.py
##Line:80
value = unicode(value, 'utf-8')
This failed with:
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 80, in _stringify
value = unicode(value, 'utf-8')
UnicodeDecodeError: 'utf8' codec can't decode byte 0xb2 in position 0: invalid start byte
So I changed it to:
###C:\Python27\Lib\lib-tk\Tkinter.py
##Line:80
value = unicode(value, 'cp950')
Then it works fine now, but does everyone have to modify their IDLE before using it?
Is this kind of a bug?
Second, my friend couldn't change IDLE's font size
Here is my debug note:
###C:\Python27\Lib\idlelib\idle.py
##Ln:10
import idlelib.PyShell
idlelib.PyShell.main()
###C:\Python27\Lib\idlelib\PyShell.py
##Ln:1475
def main():
global flist, root, use_subprocess
##Ln:1552 in function main
# start editor and/or shell windows:
root = Tk(className="Idle")
root.withdraw()
##Ln:1570 in function main
flist = PyShellFileList(root)
##Ln:312
class PyShellFileList(FileList):
"Extend base class: IDLE supports a shell and breakpoints"
# override FileList's class variable, instances return PyShellEditorWindow
# instead of EditorWindow when new edit windows are created.
EditorWindow = PyShellEditorWindow
###C:\Python27\Lib\idlelib\FileList.py
##Ln:6
class FileList:
# N.B. this import overridden in PyShellFileList.
from idlelib.EditorWindow import EditorWindow
###C:\Python27\Lib\idlelib\PyShell.py
##Ln:125
class PyShellEditorWindow(EditorWindow):
"Regular text edit window in IDLE, supports breakpoints"
def __init__(self, *args):
self.breakpoints = []
EditorWindow.__init__(self, *args)
###C:\Python27\Lib\idlelib\PyShell.py
##Ln:262 in class EditorWindow
text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow')
###C:\Python27\Lib\idlelib\configHandler.py
##Ln:700 in class IdleConf
def GetFont(self, root, configType, section):
"""Retrieve a font from configuration (font, font-size, font-bold)
Intercept the special value 'TkFixedFont' and substitute
the actual font, factoring in some tweaks if needed for
appearance sakes.
The 'root' parameter can normally be any valid Tkinter widget.
Return a tuple (family, size, weight) suitable for passing
to tkinter.Font
"""
family = self.GetOption(configType, section, 'font', default='courier')
size = self.GetOption(configType, section, 'font-size', type='int',
default='10')
bold = self.GetOption(configType, section, 'font-bold', default=0,
type='bool')
if (family == 'TkFixedFont'):
if TkVersion < 8.5:
family = 'Courier'
else:
f = Font(name='TkFixedFont', exists=True, root=root)
actualFont = Font.actual(f)
family = actualFont['family']
size = actualFont['size']
if size <= 0:
size = 10 # if font in pixels, ignore actual size
bold = actualFont['weight']=='bold'
return (family, size, 'bold' if bold else 'normal')
###C:\Python27\Lib\lib-tk\tkFont.py
##Ln:66 in class Font
def __init__(self, root=None, font=None, name=None, exists=False, **options):
if not root:
root = Tkinter._default_root
tk = getattr(root, 'tk', root)
##Ln:92 in class Font in method __init__
self._split = tk.splitlist
self._call = tk.call
##Ln:120 in class Font
def actual(self, option=None):
"Return actual font attributes"
if option:
return self._call("font", "actual", self.name, "-"+option)
else:
return self._mkdict(
self._split(self._call("font", "actual", self.name))
)
##Ln:60 in class Font
def _mkdict(self, args):
options = {}
for i in range(0, len(args), 2):
options[args[i][1:]] = args[i+1]
return options
So the problem is Tkinter ...again. I ran a small test:
import Tkinter
root=Tkinter.Tk()
def _mkdict(args):
options = {}
for i in range(0, len(args), 2):
options[args[i][1:]] = args[i+1]
return options
resulta=_mkdict(root.tk.splitlist(root.tk.call("font","actual","TkFixedFont")))
print "a",resulta
resultb=_mkdict(root.tk.splitlist(root.tk.call("font","actual","細明體")))
print "b",resultb
Then I got:
a {'family': u'\u7d30\u660e\u9ad4', 'weight': 'normal', 'slant': 'roman', 'overstrike': 0, 'underline': 0, 'size': 10}
b {'family': u'\u65b0\u7d30\u660e\u9ad4', 'weight': 'normal', 'slant': 'roman', 'overstrike': 0, 'underline': 0, 'size': 15}
So the problem is in tkFont.Font's actual module? Or...?
Edit
Maybe I didn't describe the second "problem" very well, maybe I gave too much details, so here's the core of the second problem:
###C:\Python27\Lib\idlelib\configHandler.py
##Ln:700 in class IdleConf
def GetFont(self, root, configType, section):
"""Retrieve a font from configuration (font, font-size, font-bold)
Intercept the special value 'TkFixedFont' and substitute
the actual font, factoring in some tweaks if needed for
appearance sakes.
The 'root' parameter can normally be any valid Tkinter widget.
Return a tuple (family, size, weight) suitable for passing
to tkinter.Font
"""
family = self.GetOption(configType, section, 'font', default='courier')
size = self.GetOption(configType, section, 'font-size', type='int',
default='10')
bold = self.GetOption(configType, section, 'font-bold', default=0,
type='bool')
if (family == 'TkFixedFont'):
if TkVersion < 8.5:
family = 'Courier'
else:
f = Font(name='TkFixedFont', exists=True, root=root)
actualFont = Font.actual(f)
family = actualFont['family']
size = actualFont['size']
if size <= 0:
size = 10 # if font in pixels, ignore actual size
bold = actualFont['weight']=='bold'
return (family, size, 'bold' if bold else 'normal')
If the font family is not "TkFixedFont", getFont will return the right size, which is configured by user at the "configure IDLE" panel.
But if the font family is "TkFixedFont", it'll ignore user's setting and use "actualFont['size']", which is 10, as I tested.

As to the second issue, which I believe is unrelated to the first. I don't really understand the question or the point of the code quotations.
IDLE has an Option settings dialog accessed through the menu. The initial (and default) tab has a font size setting which has always worked for me (on Windows). The answer to 'How to change fontsize?' is to use the intended font size change widget.
I just rechecked with 2.7.13 and it still works for me. There was a bug in 2.7.11 that manifested as an inability to open the dialog on some Linux systems. But that was fixed in 2.7.12. I checked the 2.7.13 patch list and none should have affected font sizing. Still, as far as IDLE goes, I would recommend upgrading.

Related

How to set font type and size for printing using windows GDI?

My python program only needs to print text in certain positions. I'm using the windows GDI to archive the goal.
The following code does the job, but i need to set the font type and the size.
import win32ui
dc = win32ui.CreateDC()
dc.CreatePrinterDC()
dc.StartDoc('Test document')
dc.StartPage()
dc.TextOut(100,2000, 'HELLO WORLD!')
dc.EndPage()
dc.EndDoc()
Use CreateFont and then select the font in to device context, example:
dc.StartPage()
fontdata = { 'name':'Arial', 'height':100, 'italic':True, 'weight':win32con.FW_NORMAL}
font = win32ui.CreateFont(fontdata);
dc.SelectObject(font)
dc.TextOut(100,2000, 'HELLO WORLD!')
...
To make a better calculation for font size, use the following based on CreateFont WinAPI reference:
def getfontsize(dc, PointSize):
inch_y = dc.GetDeviceCaps(win32con.LOGPIXELSY)
return int(-(PointSize * inch_y) / 72)
Example: now you can use this to draw with font size 11 at the center of the page
fontsize = getfontsize(dc, 11)
fontdata = { 'name':'Consolas', 'height':fontsize}
font = win32ui.CreateFont(fontdata);
dc.SelectObject(font)
Also, LOGPIXELSY/LOGPIXELSY and PHYSICALWIDTH/PHYSICALHEIGHT can be used to calculate the coordinates more accurately.

How does platypus "guess" bold and italic styles?

Here's my code. My first function is based on the /Lib/site-packages/reportlab/lib/styles.py source code, to create an array of style:
def create_styles(p_tuples):
retour = StyleSheet1()
parent = None
for p_name, font_name, font in p_tuples:
# (!) change path if Linux:
ttf_file = "C:/Windows/Fonts/{}.ttf".format(font)
pdfmetrics.registerFont(TTFont(font_name, ttf_file))
if parent is None:
p = ParagraphStyle(name=p_name,
fontName=font_name,
fontSize=10,
leading=12)
retour.add(p)
parent = p
else:
retour.add(ParagraphStyle(name=p_name,
parent=parent,
fontName=font_name,
fontSize=10,
leading=12))
return retour
Then I build my own array with my installed fonts:
def render_to_response(self, context, **response_kwargs):
# this is a response for Django, but the question is about styles
response = HttpResponse(content_type='application/pdf; charset=utf-8')
# ! Hint : no filename -> the browser extracts it from the URL!
# -> create URLs like http://pdfreportlab.com/extract/myfilename.pdf
# this is the way to go to have 100% working UTF-8 filenames!
response['Content-Disposition'] = 'attachment; filename=""'
my_styles = self.create_styles([
('ms-regular', 'montserrat-regular',
'Montserrat-Regular'),
('ms-black', 'montserrat-black',
'Montserrat-Black'),
('ms-black-italic', 'montserrat-black-italic',
'Montserrat-BlackItalic'),
('ms-bold', 'montserrat-bold',
'Montserrat-Bold'),
('ms-bold-italic', 'montserrat-bold-italic',
'Montserrat-BoldItalic'),
])
doc = SimpleDocTemplate(response)
elements = []
c = canvas.Canvas(response, pagesize=A4, )
for idx in my_styles.byName:
p = Paragraph("Hello World <i>italic</i> <b>bold</b>",
style=my_styles[idx])
width, height = p.wrapOn(c, A4[0], A4[1])
elements.append(p)
doc.build(elements)
return response
Everything is working except the (very annoying) fact that the <i></i> and <b></b> tags are ignored! It only uses the current font in the style.
How could you modify my code so that it takes in account the tags and I finally get styles with the tags in the text itself?
You need to register a font family if you want Platypus to auto-select fonts - you're creating a different style for every font variant so of course the <i> and <b> tags have no effect - it doesn't know what fonts to use for them as the passed style references a single font.
Here's how to build a style using a font family:
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase.pdfmetrics import registerFont, registerFontFamily
from reportlab.pdfbase.ttfonts import TTFont
def create_paragraph_style(name, font_name, **kwargs):
ttf_path = "C:/Windows/Fonts/{}.ttf"
family_args = {} # store arguments for the font family creation
for font_type in ("normal", "bold", "italic", "boldItalic"): # recognized font variants
if font_type in kwargs: # if this type was passed...
font_variant = "{}-{}".format(font_name, font_type) # create font variant name
registerFont(TTFont(font_variant, ttf_path.format(kwargs[font_type])))
family_args[font_type] = font_variant # add it to font family arguments
registerFontFamily(font_name, **family_args) # register a font family
return ParagraphStyle(name=name, fontName=font_name, fontSize=10, leading=12)
Then you can create your paragraph style as:
pstyle = create_paragraph_style("ms", "montserrat",
normal="Montserrat-Regular",
bold="Montserrat-Bold",
boldItalic="Montserrat-BoldItalic")
assuming, of course, that you have TTF files with those names in your fonts directory.
You can then add it to your stylesheet (especially if you want parent inheritance - make sure you add it as an argument to be forwarded to the ParagraphStyle creation) or directly use it as:
p = Paragraph("Hello World <i>italic</i> <b>bold</b> <b><i>boldItalic</i></b>", style=pstyle)
(the standalone italic won't be affected as we didn't define it in the family, tho).

Python GTK Color Chooser Widget - Set Color

I am writing a program using Python3 and GTK3 (via gi.repository). I want the color chooser to change its selected color when a RGB value is typed in the entry box and "Convert" is clicked. The "set_rgba()" command (found at http://learngtk.org/tutorials/python_gtk3_tutorial/html/colorchooser.html) is not changing the selected color. No error messages are generated (I executed the Python script from a terminal).
The file contains functions for each button, so I will include the relevant snippets of code.
class ColorWin():
"""Color Dialog"""
def __init__(self):
self.ui = Gtk.Builder()
self.ui.add_from_string(buffer=_GCOLOR)
global _cc
global _entry_rgb, _entry_hsi, _entry_hsl
global _entry_hsv, _entry_cmyk, _entry_yiq
_cc = self.ui.get_object('cc')
_entry_rgb = self.ui.get_object('entry_rgb')
_entry_hsi = self.ui.get_object('entry_hsi')
_entry_hsl = self.ui.get_object('entry_hsl')
_entry_hsv = self.ui.get_object('entry_hsv')
_entry_cmyk = self.ui.get_object('entry_cmyk')
_entry_yiq = self.ui.get_object('entry_yiq')
# Match signal to function (handler)
dic = {
'_winexit' : Gtk.main_quit,
'_submit_color': self._submit_color,
'conv_color': self.conv_color,
'conv_rgb': self.conv_rgb,
'conv_hsi': self.conv_hsi,
'conv_hsl': self.conv_hsl,
'conv_hsv': self.conv_hsv,
'conv_cmyk': self.conv_cmyk,
'conv_yiq': self.conv_yiq,
}
self.ui.connect_signals(dic)
The function for the convert button
def conv_rgb(self, _entry_rgb):
"""Convert RGB to *"""
_rgb = _entry_rgb.get_text()
_round = 6
_red = re.sub('\(([0-9.]+), ([0-9.]+), ([0-9.]+)\)', r'\1', _rgb)
_green = re.sub('\(([0-9.]+), ([0-9.]+), ([0-9.]+)\)', r'\2', _rgb)
_blue = re.sub('\(([0-9.]+), ([0-9.]+), ([0-9.]+)\)', r'\3', _rgb)
_red = round(float(_red), _round)
_green = round(float(_green), _round)
_blue = round(float(_blue), _round)
_rgba_gdk = Gdk.RGBA(_red, _green, _blue, 1.000)
_cc.set_rgba(_rgba_gdk)
I have used print functions to print the value of each variable to the terminal. I have verified that the values and datatypes are correct. The "_rgba_gdk" is an Gdk.RGBA object (as it should be). "_cc" is the color chooser. I can use _cc.get_rgba() to get the currently selected value. However, I want to change it (via _cc.set_rgba(_rgba_gdk)) to the value in the RGB entry box (gotten from _entry_rgb.get_text()). This will allow users to see the color associated with the typed RGB value (the alpha is assumed to be 1 if no alpha is specified).
The problem seems to be get/set_rgba() is working with the currently selected color in the widgets "swatch" mode but not the editor mode (show-editor=True). When in editor mode, changing the editor updates the current color as well, but the data binding is not bi-directional. All I can offer is a hack which forces the editor to update after a new color is set:
from gi.repository import Gtk
from gi.repository import Gdk
window = Gtk.Window()
window.connect("destroy", Gtk.main_quit)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
window.add(box)
colorchooser = Gtk.ColorChooserWidget(show_editor=True)
box.add(colorchooser)
entry = Gtk.Entry(text='0.5, 0.5, 0.5, 1.0')
box.add(entry)
def on_button_clicked(button):
values = [float(v) for v in entry.get_text().split(',')]
colorchooser.set_rgba(Gdk.RGBA(*values))
colorchooser.set_property("show-editor", True)
button = Gtk.Button(label="Parse RGBA")
button.connect("clicked", on_button_clicked)
box.add(button)
window.show_all()
Gtk.main()
Note colorchooser.set_property("show-editor", True) in the button clicked callback. This may or may not work in all versions of GTK+. I would log this as a bug requesting the color editor mode be updated if set_rgba() is called:
https://bugzilla.gnome.org/enter_bug.cgi?product=gtk%2B

Drag and drop explorer files to tkinter entry widget?

I'm fairly new to Python. I'm trying to input a file name (complete with full path) to a TKinter entry widget.
Since the path to the file name can be very long I would like to be able to drag and drop the file directly
from Windows Explorer. In Perl I have seen the following:
use Tk::DropSite;
.
.
my $mw = new MainWindow;
$top = $mw->Toplevel;
$label_entry = $top->Entry(-width => '45',. -background => 'ivory2')->pack();
$label_entry->DropSite(-dropcommand => \&drop,-droptypes => 'Win32',);
Is there something similar I can do using TKinter in Python?
Tk does not have any command to handle that, and Python doesn't include any extra Tk extension to perform drag & drop inter-applications, therefore you need an extension to perform the operation. Tkdnd (the Tk extension at http://sourceforge.net/projects/tkdnd, not the Tkdnd.py module) works for me. To use it from Python, a wrapper is required. Quickly searching for one, it seems http://mail.python.org/pipermail/tkinter-discuss/2005-July/000476.html contains such code. I did another one because I didn't like that other one. The problem with my wrapper is that it is highly untested, in fact I only used the function bindtarget and only for 10 seconds or so.
With the wrapper below, you can create some widget and announce that it supports receiving dragged files. Here is one example:
# The next two lines are not necessary if you installed TkDnd
# in a proper place.
import os
os.environ['TKDND_LIBRARY'] = DIRECTORYTOTHETKDNDBINARY
import Tkinter
from untested_tkdnd_wrapper import TkDND
root = Tkinter.Tk()
dnd = TkDND(root)
entry = Tkinter.Entry()
entry.pack()
def handle(event):
event.widget.insert(0, event.data)
dnd.bindtarget(entry, handle, 'text/uri-list')
root.mainloop()
And here is the code for untested_tkdnd_wrapper.py:
import os
import Tkinter
def _load_tkdnd(master):
tkdndlib = os.environ.get('TKDND_LIBRARY')
if tkdndlib:
master.tk.eval('global auto_path; lappend auto_path {%s}' % tkdndlib)
master.tk.eval('package require tkdnd')
master._tkdnd_loaded = True
class TkDND(object):
def __init__(self, master):
if not getattr(master, '_tkdnd_loaded', False):
_load_tkdnd(master)
self.master = master
self.tk = master.tk
# Available pre-defined values for the 'dndtype' parameter:
# text/plain
# text/plain;charset=UTF-8
# text/uri-list
def bindtarget(self, window, callback, dndtype, event='<Drop>', priority=50):
cmd = self._prepare_tkdnd_func(callback)
return self.tk.call('dnd', 'bindtarget', window, dndtype, event,
cmd, priority)
def bindtarget_query(self, window, dndtype=None, event='<Drop>'):
return self.tk.call('dnd', 'bindtarget', window, dndtype, event)
def cleartarget(self, window):
self.tk.call('dnd', 'cleartarget', window)
def bindsource(self, window, callback, dndtype, priority=50):
cmd = self._prepare_tkdnd_func(callback)
self.tk.call('dnd', 'bindsource', window, dndtype, cmd, priority)
def bindsource_query(self, window, dndtype=None):
return self.tk.call('dnd', 'bindsource', window, dndtype)
def clearsource(self, window):
self.tk.call('dnd', 'clearsource', window)
def drag(self, window, actions=None, descriptions=None,
cursorwin=None, callback=None):
cmd = None
if cursorwin is not None:
if callback is not None:
cmd = self._prepare_tkdnd_func(callback)
self.tk.call('dnd', 'drag', window, actions, descriptions,
cursorwin, cmd)
_subst_format = ('%A', '%a', '%b', '%D', '%d', '%m', '%T',
'%W', '%X', '%Y', '%x', '%y')
_subst_format_str = " ".join(_subst_format)
def _prepare_tkdnd_func(self, callback):
funcid = self.master.register(callback, self._dndsubstitute)
cmd = ('%s %s' % (funcid, self._subst_format_str))
return cmd
def _dndsubstitute(self, *args):
if len(args) != len(self._subst_format):
return args
def try_int(x):
x = str(x)
try:
return int(x)
except ValueError:
return x
A, a, b, D, d, m, T, W, X, Y, x, y = args
event = Tkinter.Event()
event.action = A # Current action of the drag and drop operation.
event.action_list = a # Action list supported by the drag source.
event.mouse_button = b # Mouse button pressed during the drag and drop.
event.data = D # The data that has been dropped.
event.descr = d # The list of descriptions.
event.modifier = m # The list of modifier keyboard keys pressed.
event.dndtype = T
event.widget = self.master.nametowidget(W)
event.x_root = X # Mouse pointer x coord, relative to the root win.
event.y_root = Y
event.x = x # Mouse pointer x coord, relative to the widget.
event.y = y
event.action_list = str(event.action_list).split()
for name in ('mouse_button', 'x', 'y', 'x_root', 'y_root'):
setattr(event, name, try_int(getattr(event, name)))
return (event, )
Together with Tkdnd, you will find a tkdnd.tcl program which is a higher level over the own C extension it provides. I didn't wrap this higher level code, but it could be more interesting to replicate it in Python than to use this lower level wrapper.
Things have progressed since this question was originally posted, and tkdnd2.8 (Tcl extensions) and TkinterDnD2 (Python wrapper for tkdnd2.8) are readily available on SourceForge.net.
Here's minimal sample code to do exactly what you've asked.
import Tkinter
from TkinterDnD2 import *
def drop(event):
entry_sv.set(event.data)
root = TkinterDnD.Tk()
entry_sv = Tkinter.StringVar()
entry = Tkinter.Entry(root, textvar=entry_sv, width=80)
entry.pack(fill=Tkinter.X)
entry.drop_target_register(DND_FILES)
entry.dnd_bind('<<Drop>>', drop)
root.mainloop()
You can see How to Install and Use TkDnD with Python 2.7 Tkinter on OSX for download and installation info for both Windows and Mac on Python 2.7 and 3.6.
you can now simple use tkinterdnd2. I've forked it, built it, and upload it to pypi so you could simply pip install tkinterdnd2. usage examples here
or if you are too lasy here's a quick example:
import tkinter as tk
from tkinterdnd2 import DND_FILES, TkinterDnD
root = TkinterDnD.Tk() # notice - use this instead of tk.Tk()
lb = tk.Listbox(root)
lb.insert(1, "drag files to here")
# register the listbox as a drop target
lb.drop_target_register(DND_FILES)
lb.dnd_bind('<<Drop>>', lambda e: lb.insert(tk.END, e.data))
lb.pack()
root.mainloop()
this will open up this
you simply drag over files
(by the way I've used listbox instead of entry but it will work exactly the same)

Unable to use wx.NotificationMessage properly with wxPython

I recently upgraded to the development release of wxPython (wxPython 2.9.2.4) since I needed the functionality of wx.NotificationMessage within my application. I have been trying unsuccessfully to create notification bubbles on certain user events due to something I think might be a possible bug. Before submitting such bug, I wanted to go ahead and ask the people of the mailing list what they think might be the problem and hopefully find a solution from within my code.
Here is the code I have used:
import wx, sys
app = wx.PySimpleApp()
class TestTaskBarIcon(wx.TaskBarIcon):
def __init__(self):
wx.TaskBarIcon.__init__(self)
# create a test icon
bmp = wx.EmptyBitmap(16, 16)
dc = wx.MemoryDC(bmp)
dc.SetBrush(wx.RED_BRUSH)
dc.Clear()
dc.SelectObject(wx.NullBitmap)
testicon = wx.EmptyIcon()
testicon.CopyFromBitmap(bmp)
self.SetIcon(testicon)
self.Bind(wx.EVT_TASKBAR_LEFT_UP, lambda e: (self.RemoveIcon(),sys.exit()))
wx.NotificationMessage("", "Hello world!").Show()
icon = TestTaskBarIcon()
app.MainLoop()
On my Windows 7 computer, the code creates a small white task bar icon and creates a popup with the phrase "Hello World!". The problem? The message is not on my icon. Another icon is being created and the message is being placed there.
See this image:
http://www.pasteall.org/pic/18068">
What I thought was that this is probably due to the fact that I have passed no parent parameter on line 22:
wx.NotificationMessage("", "Hello world!").Show()
Here is what I changed it to:
wx.NotificationMessage("", "Hello world!", self).Show()
Where 'self' refers to the task bar icon. When I do that, I get an error:
Traceback (most recent call last):
File "C:\Python27\testnotificationmessage.py", line 24, in <module>
icon = TestTaskBarIcon()
File "C:\Python27\testnotificationmessage.py", line 22, in __init__
wx.NotificationMessage("", "Hello world!", self).Show()
File "C:\Python27\lib\site-packages\wx-2.9.2-msw\wx\_misc.py", line 1213, in __init__
_misc_.NotificationMessage_swiginit(self,_misc_.new_NotificationMessage(*args))
TypeError: in method 'new_NotificationMessage', expected argument 3 of type 'wxWindow *'
What's going on? If I remove that argument, I don't get my result, if I add the argument, I get an error! How am I supposed to use wx.NotificationMessage with a wx.TaskBarIcon!
Please help! I hope I've provided enough details. Please comment if you need more!
I would not recommend using 2.9 just yet. I have encountered some strange bugs when trying it out.
You can have the same functionality in 2.8. I am using somewhat modified code that I have found some time ago.
import wx, sys
try:
import win32gui #, win32con
WIN32 = True
except:
WIN32 = False
class BalloonTaskBarIcon(wx.TaskBarIcon):
"""
Base Taskbar Icon Class
"""
def __init__(self):
wx.TaskBarIcon.__init__(self)
self.icon = None
self.tooltip = ""
def ShowBalloon(self, title, text, msec = 0, flags = 0):
"""
Show Balloon tooltip
#param title - Title for balloon tooltip
#param msg - Balloon tooltip text
#param msec - Timeout for balloon tooltip, in milliseconds
#param flags - one of wx.ICON_INFORMATION, wx.ICON_WARNING, wx.ICON_ERROR
"""
if WIN32 and self.IsIconInstalled():
try:
self.__SetBalloonTip(self.icon.GetHandle(), title, text, msec, flags)
except Exception:
pass # print(e) Silent error
def __SetBalloonTip(self, hicon, title, msg, msec, flags):
# translate flags
infoFlags = 0
if flags & wx.ICON_INFORMATION:
infoFlags |= win32gui.NIIF_INFO
elif flags & wx.ICON_WARNING:
infoFlags |= win32gui.NIIF_WARNING
elif flags & wx.ICON_ERROR:
infoFlags |= win32gui.NIIF_ERROR
# Show balloon
lpdata = (self.__GetIconHandle(), # hWnd
99, # ID
win32gui.NIF_MESSAGE|win32gui.NIF_INFO|win32gui.NIF_ICON, # flags: Combination of NIF_* flags
0, # CallbackMessage: Message id to be pass to hWnd when processing messages
hicon, # hIcon: Handle to the icon to be displayed
'', # Tip: Tooltip text
msg, # Info: Balloon tooltip text
msec, # Timeout: Timeout for balloon tooltip, in milliseconds
title, # InfoTitle: Title for balloon tooltip
infoFlags # InfoFlags: Combination of NIIF_* flags
)
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, lpdata)
self.SetIcon(self.icon, self.tooltip) # Hack: because we have no access to the real CallbackMessage value
def __GetIconHandle(self):
"""
Find the icon window.
This is ugly but for now there is no way to find this window directly from wx
"""
if not hasattr(self, "_chwnd"):
try:
for handle in wx.GetTopLevelWindows():
if handle.GetWindowStyle():
continue
handle = handle.GetHandle()
if len(win32gui.GetWindowText(handle)) == 0:
self._chwnd = handle
break
if not hasattr(self, "_chwnd"):
raise Exception
except:
raise Exception, "Icon window not found"
return self._chwnd
def SetIcon(self, icon, tooltip = ""):
self.icon = icon
self.tooltip = tooltip
wx.TaskBarIcon.SetIcon(self, icon, tooltip)
def RemoveIcon(self):
self.icon = None
self.tooltip = ""
wx.TaskBarIcon.RemoveIcon(self)
# ===================================================================
app = wx.PySimpleApp()
class TestTaskBarIcon(BalloonTaskBarIcon):
def __init__(self):
wx.TaskBarIcon.__init__(self)
# create a test icon
bmp = wx.EmptyBitmap(16, 16)
dc = wx.MemoryDC(bmp)
dc.SetBrush(wx.RED_BRUSH)
dc.Clear()
dc.SelectObject(wx.NullBitmap)
testicon = wx.EmptyIcon()
testicon.CopyFromBitmap(bmp)
self.SetIcon(testicon)
self.Bind(wx.EVT_TASKBAR_LEFT_UP, lambda e: (self.RemoveIcon(),sys.exit()))
self.ShowBalloon("", "Hello world!")
icon = TestTaskBarIcon()
app.MainLoop()
There is an undocumented hidden method in TaskBarIcon called ShowBalloon which is only implemented for Windows.
From the source:
def ShowBalloon(*args, **kwargs):
"""
ShowBalloon(self, String title, String text, unsigned int msec=0, int flags=0) -> bool
Show a balloon notification (the icon must have been already
initialized using SetIcon). Only implemented for Windows.
title and text are limited to 63 and 255 characters respectively, msec
is the timeout, in milliseconds, before the balloon disappears (will
be clamped down to the allowed 10-30s range by Windows if it's outside
it) and flags can include wxICON_ERROR/INFO/WARNING to show a
corresponding icon
Returns True if balloon was shown, False on error (incorrect parameters
or function unsupported by OS)
"""
return _windows_.TaskBarIcon_ShowBalloon(*args, **kwargs)
I tested it on Windows with wxPython 2.9.4.0 and it works well.

Categories

Resources