How to tweak my tooltips in wxpython? - python

I was trying to add a tooltip to show the full content of a truncated ObjectListView, until it turned out it had such a feature built-in:
I tried making my own tool tips using wx.TipWindow, wx.PopupWindow and SuperToolTip, but none of them looked as 'native' as this one.
While I'm aware of this wiki article that supposedly enables the tooltip for truncated wx.Listrctrls, I didn't really understand how to get it working. I also expect that it only works when something is truncated, whereas I'd like to be able to use it to display some more information.
I guess the SuperToolTip comes close, but when you remove the 'header' it leaves it with empty space at the top, rather than centering the text in the middle of the tooltip and making it fit.
I tried looking through the source code of ObjectListView, SuperToolTip and wxpython to try and find how tooltips are being created, but I can't really find the low level parts that make it happen.
So how can I tweak tooltips so they look more like native tooltips?
The code to generate my current popups was:
text = "I'm a popup"
class PopUp(wx.TipWindow):
def __init__(self, parent, text):
wx.TipWindow.__init__(self, parent, text)
class PopUp2(wx.PopupWindow):
def __init__(self, parent, text):
wx.PopupWindow.__init__(self, parent)
st = wx.StaticText(self, parent, text)
# Import `from agw import supertooltip as STT`
popup3 = STT.SuperToolTip(text)

I'm not sure if we have a way to create a native Win7 tooltip yet, as you've seen wx.TipWindow looks like the tooltips from older versions of Windows, so there are probably some newer APIs that we should be using instead. Please create a ticket at trac.wxwidgets.org to find out for sure or to request the change if it's not possible some other way that I'm not thinking of at the moment.

Even if you can't create and pop up a native tooltip from scratch, you can still assign the entire ListCtrl a tooltip when you create it, and then change the text to whatever you want based on the item under the mouse pointer. It doesn't position the tooltip neatly over the list item like ObjectListView does, but I think it still accomplishes what you're asking.
self.lc = wx.ListCtrl(self, style=wx.LC_REPORT)
# ...
self.lc.Bind(wx.EVT_MOTION, self.OnMouseMotion)
def OnMouseMotion(self, evt):
pos = self.lc.ScreenToClient(wx.GetMousePosition())
item_index, flag = self.lc.HitTest(pos)
tip = self.lc.GetToolTip()
if flag == wx.LIST_HITTEST_ONITEMLABEL:
tip.SetTip('Some information about ' + self.lc.GetItemText(item_index))
else:
tip.SetTip('')
evt.Skip()

Related

TextCtrl scrollbar unusable until window is resized

Solved:
Thanks to Aya's answer below I now know that the issue was caused by self.panel = wx.Panel(self, -1) on line 18. I created a panel and didn't attach anything to it. The original issue description is still below for reference.
My Google-fu has failed me. I'm building the text editor that you can find here, written in Python with wxPython:
https://github.com/joshsaintjacque/py-ed/blob/master/pyed.py
The issue that I'm running into is this: when I open a text file (the only functionality built in at this point) that's larger than the viewable area in the TextCtrl the scroll bar remains disabled until the window is re-sized, then it works fine.
I know that the act of re-sizing the window is running some command that I'm neglecting to include in my OpenFile function (or perhaps in init), but I can't figure out what.
Any thoughts anyone has that could lead me in the right direction would be greatly appreciated.
Thanks!
+1 for including a link to the full source code - makes it so much easier to test.
I couldn't reproduce the fault you describe on wxPython 2.8.12 on Win32, but upon running your code, I found a seemingly extraneous wx.Panel object being created on pyed.py line 18...
self.panel = wx.Panel(self, -1)
...which seems to be interfering with the correct operation of the program. After commenting out that line, it seems to work fine.
A couple of other things I noticed: line 56...
self.SetTitle("PyEd - Editing ... " + filename)
...should probably be put in the preceding if-block, otherwise you'll get an error if the user clicks "Cancel" on the wx.FileDialog, and on line 16...
wx.Frame.__init__(self, parent, id, 'PyEd', (-1, -1), wx.Size(640, 480))
...if you use keyword args rather than positional args...
wx.Frame.__init__(self, parent=parent, id=id, title='PyEd', size=wx.Size(640, 480))
...you needn't bother re-specifying the default value for the window position, which is also slightly safer, in case the wxPython developers decide to change the defaults in a future version.
You can also factor out constant values, and the optional creation of the wx.Size object to reduce that line to...
wx.Frame.__init__(self, parent=None, title='PyEd', size=(640, 480))
Finally, with regards to IDs: in most cases you'll probably find they're of little use. Where they come in handy is where you want many similar controls, and it makes more sense to have them handled by a single event handler function.
Consider this example...
def create_buttons(parent):
parent.button1 = wx.Button(label='Button 1')
parent.button2 = wx.Button(label='Button 2')
parent.button3 = wx.Button(label='Button 3')
parent.button1.Bind(wx.EVT_BUTTON, on_button_1)
parent.button2.Bind(wx.EVT_BUTTON, on_button_2)
parent.button3.Bind(wx.EVT_BUTTON, on_button_3)
def on_button_1(event):
print 'You clicked button 1'
def on_button_2(event):
print 'You clicked button 2'
def on_button_3(event):
print 'You clicked button 3'
...which is fine, but if you need, say, 100 buttons, you may prefer to implement it like this...
def create_buttons(parent):
parent.buttons = [wx.Button(id=i, label='Button %d' % i) for i in range(100)]
parent.Bind(wx.EVT_BUTTON, on_button)
def on_button(event):
button_id = event.GetId()
print 'You clicked button %d' % button_id
Oh, and be careful using id as a variable name, because it's also a Python built-in function name.
It looks as if you're not setting the min or max size hints for the window, nor are you calling Self.Fit() to fit the box sizer to the window size (or is it the other way round? I'm rusty on my wxPython...)
Right where you call self.SetSizer(sizer), you should be able to fix this by adding:
self.Fit()
self.SetSizeHintSz(minSize=wx.Size(640, 480))
You may be able to get around the separate call to self.Fit() by using self.SetSizerAndFit()
(edited for spelling.)

wxPython error message - clicking button for a small image to appear on the canvas within my frame

OK, so iv almost completed my program for my project but I cant get a BUTTON_EVT to work which if i am honest should be the easiest thing todo. I have the buttons on my program which represent the hardware and I have created a def function for them to appear on the OGL canvas.
Problem has been solved... The code associated with the problem is found in the answer below
Edited from your last comment. Use this (using your own images):
def OnClickRouter(self, event):
image=wx.Image('cat.jpg', wx.BITMAP_TYPE_JPEG)
self.frame = bucky(None, image)
self.frame.Show()
If you call bucky() this way you must also fix the class signature:
class bucky(wx.Frame):
# Creating the outer window/frame
def __init__(self, parent, image=None):
wx.Frame.__init__(self, parent, -1,'Karls Network Tool', size=(900,700))
my_image = image if image else wx.Image("myself.bmp", wx.BITMAP_TYPE_BMP)
''''''''''''''''''''''''''''''''
# Button images
buttonOneRouter = my_image.ConvertToBitmap()
self.buttonOneRouter = wx.BitmapButton(panel, -1, buttonOneRouter, pos=(20,340))
self.buttonOneRouter.Bind(wx.EVT_BUTTON, self.OnClickRouter)
''''''''''''''''''''''''''''''''
Then you can see that after clicking the buttonOnerouter what actually you are doing is opening a new frame. The left figure is what I get when I run the program, the right one is after I click and enter again my name (I simplified a bit your code. Thats why you only see one button at the bottom instead of 4):
If you want to put my cat in the canvas instead of in the button there is still some work to do. I recommend to you to give a look at the wxPython demo. In the miscellaneous group of examples you have one called OGL that shows how to do that.
Edit: You can download the wxPython docs and demos package from here
I don't know if this is right or not but I suggest you to take this approach and see if it works or not.
Modify your frame class as:
def __init(self,parent,id,img=None)
def onClickRouter(self,event):
image=wx.Image('router.jpg', wx.BITMAP_TYPE_JPEG)
temp = image.ConvertToBitmap()
self.bmp = wx.StaticBitmap(parent=self, bitmap=temp)
self.frame=bucky(self.bmp)
Please let know the outcome.

PyQt - How to turn on/off spellcheck highlighting

I have a button which sets/unsets spellcheck highlighting in a QTextEdit box (ref PyQt - How to turn on/off spellchecking) which works fine.
Then I added a language selection QComboBox and tied its signal to the button's property but its highlighting set/unset doesn't work on changing the language. It drives me nuts, there may be something small and stupid I've done, but for the sake of it I can't find anything wrong with it.
The button (action rather) is
self.actionSpellCheck = QAction(QIcon(self.icon_spellcheck),
"Auto &Spellcheck", self,
shortcut=Qt.CTRL + Qt.SHIFT + Qt.Key_O,
triggered=self.spellcheck, checkable=True)
The combobox is
self.cb_lang = QComboBox(tb)
tb.addWidget(self.cb_lang)
lang_list = self.dict_broker.list_languages()
self.cb_lang.addItems(lang_list)
self.cb_lang.currentIndexChanged.connect(self.spellcheck)
and the self.spellcheck is
def spellcheck(self):
pos = self.cursor.position()
if self.actionSpellCheck.isChecked():
lang = self.cb_lang.currentText()
self.dict = self.dict_broker.request_dict(lang)
self.highlighter.setDict(self.dict)
self.setHighlighterEnabled(True)
self.show_status("Spellcheck language is set to " + self.dict.tag, None)
else:
self.setHighlighterEnabled(False)
self.highlighter.setDict(None)
self.show_status("Spellcheck is turned off", None)
self.cursor.setPosition(pos, QTextCursor.MoveAnchor)
self.textEdit.setTextCursor(self.cursor)
self.textEdit.setFocus()
How come the highlighter gets set/unset on clicking the button, but nothing happens on selecting the language (it only happens after I start typing, not immediately on combobox selection)? Thank you.
If you look at the HighLighter.setDict method, you'lll see that it doesn't do much other than reassign the dict attribute.
Also, the SpellTextEdit.setHighlighterEnabled only resets the document.
So you're going to need a method to re-highlight the text whenever the dict changes. Fortunately, HighLighter is a subclass of QSyntaxHighlighter, which already has a rehighlight slot which does what is required.
So you just need to amend your spellcheck method as follows:
def spellcheck(self):
pos = self.cursor.position()
if self.actionSpellCheck.isChecked():
self.setHighlighterEnabled(True)
lang = self.cb_lang.currentText()
self.dict = self.dict_broker.request_dict(lang)
self.highlighter.setDict(self.dict)
self.highlighter.rehighlight()
else:
...

How to get (childless) “tabs” in a pygtk application

I am facing the problem to need tabs in a pygtk app. Pretty much just like gedit has, but without any per-child widget content.
I’ve come across gtk.Notebook, but that requires me to put a widget for each tab, which I don't want.
The reason is, that I have one widget, but would only like to updates its content based on which tab is selected.
Any hints on how to do that?
My idea so far would be to just add some invisible widget for each tab and then connect to the select-page signal. Which widget could I use as invisible widget, or is there a better/alternative way of achieving my goal?
The invisble widget idea works. But not with gtk.Invisible (this just crashes), but with gtk.HBox() or any other thing that seems empty.
self.notebook.append_page(gtk.HBox(), gtk.Label("title"))
Now if I want to display stuff inside the tab actually, I can use reparent to move the widget to the current tab like this.
class Tab(gtk.HBox):
def __init__(self, child):
self.child = child
self.notebook.append_page(Tab(myWidget), gtk.Label("title"))
def pageSelected(self, notebook, page, pagenum):
box = notebook.get_nth_page(pagenum)
box.child.reparent(box)
You can have global widgets, one per tab as you want, in order to access them easily when the tab is selected.
self.notebook.append_page(self.rightBox, gtk.Label("Orders"))
Then connect to the "switch page" signal
self.notebook.connect("switch-page", self.pageSelected)
and :
def pageSelected(self, notebook, page, pagenum):
name = notebook.get_tab_label(notebook.get_nth_page(pagenum))
Now you have "name" with the label of the currently selected page. Just test it (if name == "Orders" ...) to interact.
Hope this was of some help !

wxPython, Set value of StaticText()

I am making a little GUI frontend for a app at the moment using wxPython.
I am using wx.StaticText() to create a place to hold some text, code below:
content = wx.StaticText(panel, -1, "Text Here", style=wx.ALIGN_CENTRE)
I have a button when clicked retrieves data from MySQL, I am wanting to change the value of the StaticText() to the MySQL data or what else could I use the hold the data.
I have tried using the below method:
contents = wx.TextCtrl(bkg, style=wx.TE_MULTILINE | wx.HSCROLL)
content.SetValue("New Text")
This displays the data fine but after the data is loaded you can edit the data and I do not want this.
Hope you guys understand what I am trying to do, I am new to Python :)
Cheers
If you are using a wx.StaticText() you can just:
def __init__(self, parent, *args, **kwargs): #frame constructor, etc.
self.some_text = wx.StaticText(panel, wx.ID_ANY, label="Awaiting MySQL Data", style=wx.ALIGN_CENTER)
def someFunction(self):
mysql_data = databasemodel.returnData() #query your database to return a string
self.some_text.SetLabel(mysql_data)
As litb mentioned, the wxWidgets docs are often much easier to use than the wxPython docs. In order to see that the SetLabel() function can be applied to a wx.StaticText instance, you have to travel up the namespace hierarchy in the wxPython docs to the wxWindow superclass, from which wx.StaticText is subclassed. There are a few things different in wxPython from wxWidgets, and it can be challenging to find out what they are. Fortunately, a lot of the time, the differences are convenience functions that have been added to wxPython and are not found in wxWidgets.
wx.TextCtrl has a style called wx.TE_READONLY . Use that to make it read-only.
As a sidenode, you can use the C++ wxWidgets Manual for wxPython aswell. Where special handling for wxPython or other ports is required, the manual often points out the difference.

Categories