geekdoc-python-zh/docs/pythonlibrary/wxpython-adding-tooltips-ob...

13 KiB

wxPython:向 ObjectListView 添加工具提示

原文:https://www.blog.pythonlibrary.org/2013/12/12/wxpython-adding-tooltips-objectlistview/

wxOLVTooltips

最近,我试图弄清楚如何在 Windows 上的 wxPython 中为 ObjectListView 小部件中的每个项目添加工具提示。wxPython wiki 有一个使用 PyWin32 的例子,但是我不想走这条路。所以我在 wxPython 谷歌群上问了一下,得到了一个有趣的答案。他们实际上使用了我的一篇旧的文章来为我构建他们的解决方案。我稍微整理了一下,觉得值得与我的读者分享:


import wx
from ObjectListView import ObjectListView, ColumnDefn

########################################################################
class Book(object):
    """
    Model of the Book object

    Contains the following attributes:
    'ISBN', 'Author', 'Manufacturer', 'Title'
    """
    #----------------------------------------------------------------------
    def __init__(self, title, author, isbn, mfg):
        self.isbn = isbn
        self.author = author
        self.mfg = mfg
        self.title = title

########################################################################
class MainPanel(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
        self.products = [Book("wxPython in Action", "Robin Dunn",
                              "1932394621", "Manning"),
                         Book("Hello World", "Warren and Carter Sande",
                              "1933988495", "Manning")
                         ]

        self.dataOlv = ObjectListView(self, wx.ID_ANY, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.setBooks()

        # Allow the cell values to be edited when double-clicked
        self.dataOlv.cellEditMode = ObjectListView.CELLEDIT_SINGLECLICK

        # create an update button
        updateBtn = wx.Button(self, wx.ID_ANY, "Update OLV")
        updateBtn.Bind(wx.EVT_BUTTON, self.updateControl)

        # Create some sizers
        mainSizer = wx.BoxSizer(wx.VERTICAL)        

        mainSizer.Add(self.dataOlv, 1, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(updateBtn, 0, wx.ALL|wx.CENTER, 5)
        self.SetSizer(mainSizer)

        self.dataOlv.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onSetToolTip)

    #----------------------------------------------------------------------
    def updateControl(self, event):
        """
        Update the control
        """
        print "updating..."
        #product_dict = [{"title":"Core Python Programming", "author":"Wesley Chun",
                         #"isbn":"0132269937", "mfg":"Prentice Hall"},
                        #{"title":"Python Programming for the Absolute Beginner",
                         #"author":"Michael Dawson", "isbn":"1598631128",
                         #"mfg":"Course Technology"},
                        #{"title":"Learning Python", "author":"Mark Lutz",
                         #"isbn":"0596513984", "mfg":"O'Reilly"}
                        #]

        product_list = [Book("Core Python Programming", "Wesley Chun",
                             "0132269937", "Prentice Hall"),
                        Book("Python Programming for the Absolute Beginner",
                             "Michael Dawson", "1598631128", "Course Technology"),
                        Book("Learning Python", "Mark Lutz", "0596513984",
                             "O'Reilly")
                        ]

        data = self.products + product_list
        self.dataOlv.SetObjects(data)

    #----------------------------------------------------------------------
    def setBooks(self, data=None):
        """
        Sets the book data for the OLV object
        """
        self.dataOlv.SetColumns([
            ColumnDefn("Title", "left", 220, "title"),
            ColumnDefn("Author", "left", 200, "author"),
            ColumnDefn("ISBN", "right", 100, "isbn"),
            ColumnDefn("Mfg", "left", 180, "mfg")
        ])

        self.dataOlv.SetObjects(self.products)

    #----------------------------------------------------------------------
    def onSetToolTip(self, event):
        """
        Set the tool tip on the selected row
        """
        item = self.dataOlv.GetSelectedObject()
        tooltip = "%s is a good writer!" % item.author
        event.GetEventObject().SetToolTipString(tooltip)
        event.Skip()

########################################################################
class MainFrame(wx.Frame):
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, 
                          title="ObjectListView Demo", size=(800,600))
        panel = MainPanel(self)

########################################################################
class GenApp(wx.App):

    #----------------------------------------------------------------------
    def __init__(self, redirect=False, filename=None):
        wx.App.__init__(self, redirect, filename)

    #----------------------------------------------------------------------
    def OnInit(self):
        # create frame here
        frame = MainFrame()
        frame.Show()
        return True

#----------------------------------------------------------------------
def main():
    """
    Run the demo
    """
    app = GenApp()
    app.MainLoop()

if __name__ == "__main__":
    main()

我需要做的就是添加一个绑定到 wx。EVT _ 列表 _ 项目 _ 选定。然后在我的事件处理程序中,我需要获取事件对象并设置它的工具提示字符串。我真正想做的是找到一种方法来复制这个网格配方,这样我就可以将鼠标放在项目上并改变工具提示,但是看起来 ObjectListView / ListCtrl 没有我需要的方法来将鼠标坐标转换成列/行。不管怎样,给出的解决方案确实像宣传的那样有效。多谢了,二信!

Update: 我的一位精明的读者注意到了我的代码中的一个错误,当我点击 Update 按钮时,它向 ObjectListView 小部件添加了一个字典。虽然您可以添加字典,但这破坏了 onSetToolTip 方法,因为添加的一些条目不再是 Book 实例。因此,我已经更新了代码,添加了额外的条目作为 Book 实例,并注释掉了 dictionary 示例。

更新(2013/12/30)

在玩了一个我找到的 StackOverflow 答案之后,我发现我可以动态更新工具提示,尽管它仍然不是我想要的。但是首先,下面是更新后的代码示例:


import wx
from ObjectListView import ObjectListView, ColumnDefn

########################################################################
class Book(object):
    """
    Model of the Book object

    Contains the following attributes:
    'ISBN', 'Author', 'Manufacturer', 'Title'
    """
    #----------------------------------------------------------------------
    def __init__(self, title, author, isbn, mfg):
        self.isbn = isbn
        self.author = author
        self.mfg = mfg
        self.title = title

########################################################################
class MainPanel(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
        self.products = [Book("wxPython in Action", "Robin Dunn",
                              "1932394621", "Manning"),
                         Book("Hello World", "Warren and Carter Sande",
                              "1933988495", "Manning")
                         ]

        self.dataOlv = ObjectListView(self, wx.ID_ANY, 
                                      style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.dataOlv.Bind(wx.EVT_MOTION, self.updateTooltip)
        self.setBooks()

        # Allow the cell values to be edited when double-clicked
        self.dataOlv.cellEditMode = ObjectListView.CELLEDIT_SINGLECLICK

        # create an update button
        updateBtn = wx.Button(self, wx.ID_ANY, "Update OLV")
        updateBtn.Bind(wx.EVT_BUTTON, self.updateControl)

        # Create some sizers
        mainSizer = wx.BoxSizer(wx.VERTICAL)        

        mainSizer.Add(self.dataOlv, 1, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(updateBtn, 0, wx.ALL|wx.CENTER, 5)
        self.SetSizer(mainSizer)

    #----------------------------------------------------------------------
    def updateControl(self, event):
        """
        Update the control
        """
        print "updating..."
        #product_dict = [{"title":"Core Python Programming", "author":"Wesley Chun",
                         #"isbn":"0132269937", "mfg":"Prentice Hall"},
                        #{"title":"Python Programming for the Absolute Beginner",
                         #"author":"Michael Dawson", "isbn":"1598631128",
                         #"mfg":"Course Technology"},
                        #{"title":"Learning Python", "author":"Mark Lutz",
                         #"isbn":"0596513984", "mfg":"O'Reilly"}
                        #]

        product_list = [Book("Core Python Programming", "Wesley Chun",
                             "0132269937", "Prentice Hall"),
                        Book("Python Programming for the Absolute Beginner",
                             "Michael Dawson", "1598631128", "Course Technology"),
                        Book("Learning Python", "Mark Lutz", "0596513984",
                             "O'Reilly")
                        ]

        data = self.products + product_list
        self.dataOlv.SetObjects(data)

    #----------------------------------------------------------------------
    def setBooks(self, data=None):
        """
        Sets the book data for the OLV object
        """
        self.dataOlv.SetColumns([
            ColumnDefn("Title", "left", 220, "title"),
            ColumnDefn("Author", "left", 200, "author"),
            ColumnDefn("ISBN", "right", 100, "isbn"),
            ColumnDefn("Mfg", "left", 180, "mfg")
        ])

        self.dataOlv.SetObjects(self.products)

    #----------------------------------------------------------------------
    def updateTooltip(self, event):
        """
        Update the tooltip!
        """
        pos = wx.GetMousePosition()
        mouse_pos = self.dataOlv.ScreenToClient(pos)
        item_index, flag = self.dataOlv.HitTest(mouse_pos)
        print flag
        if flag == wx.LIST_HITTEST_ONITEMLABEL:
            msg = "%s is a good book!" % self.dataOlv.GetItemText(item_index)
            self.dataOlv.SetToolTipString(msg)
        else:
            self.dataOlv.SetToolTipString("")

        event.Skip()

########################################################################
class MainFrame(wx.Frame):
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, 
                          title="ObjectListView Demo", size=(800,600))
        panel = MainPanel(self)

#----------------------------------------------------------------------
def main():
    """
    Run the demo
    """
    app = wx.App(False)
    frame = MainFrame()
    frame.Show()
    app.MainLoop()

#---------------------------------------------------------------------- 
if __name__ == "__main__":
    main()

这里的主要变化是移除 setToolTip 方法并添加一个 updateTooltip 方法。在上述方法中,我们获取鼠标位置并使用它来更新工具提示。我对这种方法的问题是,工具提示只有在您将鼠标悬停在第一列的单元格上时才会更新。除此之外,它工作得很好。如果你碰巧发现了其他的方法,请在评论中告诉我。

更新(2014/01/23) :

我的一个读者今天联系我,告诉我对这段代码的另一个修正。他设法找到了添加工具提示的方法,这样无论鼠标悬停在行的哪一部分,工具提示都会出现。变化很简单。对 updateTooltip 方法的更改。以下是原始版本:


def updateTooltip(self, event):
    """
    Update the tooltip!
    """
    pos = wx.GetMousePosition()
    mouse_pos = self.dataOlv.ScreenToClient(pos)
    item_index, flag = self.dataOlv.HitTest(mouse_pos)
    print flag
    if flag == wx.LIST_HITTEST_ONITEMLABEL:
        msg = "%s is a good book!" % self.dataOlv.GetItemText(item_index)
        self.dataOlv.SetToolTipString(msg)
    else:
        self.dataOlv.SetToolTipString("")

    event.Skip()

以下是修复版本:


def updateTooltip(self, event):
    """
    Update the tooltip!
    """
    pos = wx.GetMousePosition()
    mouse_pos = self.dataOlv.ScreenToClient(pos)
    item_index, flag = self.dataOlv.HitTest(mouse_pos)
    print flag
    if item_index != -1:
        msg = "%s is a good book!" % self.dataOlv.GetItemText(item_index)
        self.dataOlv.SetToolTipString(msg)
    else:
        self.dataOlv.SetToolTipString("")

    event.Skip()

对 if 语句的修改是,我们现在看一下 item_index 。实际上我不知道这为什么有效,但对我来说确实有效。特别感谢迈克·斯托弗发现了这一点。