Best way to show truncated data in ObjectListView cell - python

One of the columns in my ObjectListView contains data that is too long to display completely, so it's automatically truncated. I would like a way to display all the data in the cell. I've already implemented the suggestion in this tutorial, but I'm not very happy with the result because the tooltip is for the entire list, not just that list item. Ideally, I'd like to have exactly what's pictured in the first image of this other question.
That post mentions that the expansion-on-hover behavior happens automatically, but I don't see any way of recreating that behavior. Perhaps it only happens on Windows? I'm running on GTK.

You could always implement an on right click pop-up window that you can populate with whatever you would like - see the wxPython Demo for Pop-up windows as a starting point.
Alternatively you could switch from ObjectListView to wx.lib.agw.UltimateListCtrl or DataViewControl - both of these seem to support tooltips that are aware of which row and column you are hovering over.

I messed around with the code in my tutorial using the answer you had mentioned and came up with the following:
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):
"""
"""
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()
I also updated my tutorial to include the code above. Please note that the tooltip only updates when you mouse through the cells in the first column. I haven't found a good way around that.

Related

wx.Hypertreelist - How add widgets to it? / How make texts editable?

Recently i found Hypertreelist, it seems to be what i Need, but there arent any good examples. So i have a few questions:
How do I add wx.Widgets to it? Like placing a Combobox
How do i make the Texts inside a row editable? Vie Doubleclick would be good
Here is my Code so far:
import wx
import wx.lib.agw.hypertreelist as HTL
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "HyperTreeList Demo")
tree_list = HTL.HyperTreeList(self)
tree_list.AddColumn("First column")
root = tree_list.AddRoot("Root")
parent = tree_list.AppendItem(root, "First child")
child = tree_list.AppendItem(parent, "First Grandchild")
tree_list.AppendItem(root, "Second child")
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
You could try it with a HitTest and EditLabel. The code could look like this:
pt = event.GetPosition()
item, flags, column = self.tree.HitTest(pt)
if "CP" in item.GetText():
if column > 1:
self.tree.EditLabel(item, column)
event.Skip()

wxPython: Switching between multiple panels with a button

I would like to have two (I will add more later) panels that occupy the same space within the frame and for them to be shown/hidden when the respective button is pressed on the toolbar, "mListPanel" should be the default. Currently the settings panel is shown when the application is launched and the buttons don't do anything. I've searched and tried lots of stuff for hours and still can't get it to work. I apologise if it's something simple, I've only started learning python today.
This is what the code looks like now:
import wx
class mListPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
#wx.StaticText(self, -1, label='Search:')#, pos=(10, 3))
#wx.TextCtrl(self, pos=(10, 10), size=(250, 50))
class settingsPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
class bifr(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Title")
self.listPanel = mListPanel(self)
self.optPanel = settingsPanel(self)
menuBar = wx.MenuBar()
fileButton = wx.Menu()
importItem = wx.Menu()
fileButton.AppendMenu(wx.ID_ADD, 'Add M', importItem)
importItem.Append(wx.ID_ANY, 'Import from computer')
importItem.Append(wx.ID_ANY, 'Import from the internet')
exitItem = fileButton.Append(wx.ID_EXIT, 'Exit')
menuBar.Append(fileButton, 'File')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.Quit, exitItem)
toolBar = self.CreateToolBar()
homeToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Home', wx.Bitmap('icons/home_icon&32.png'))
importLocalToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Import from computer', wx.Bitmap('icons/comp_icon&32.png'))
importToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Import from the internet', wx.Bitmap('icons/arrow_bottom_icon&32.png'))
settingsToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'settings', wx.Bitmap('icons/wrench_plus_2_icon&32.png'))
toolBar.Realize()
self.Bind(wx.EVT_TOOL, self.switchPanels(), settingsToolButton)
self.Bind(wx.EVT_TOOL, self.switchPanels(), homeToolButton)
self.Layout()
def switchPanels(self):
if self.optPanel.IsShown():
self.optPanel.Hide()
self.listPanel.Show()
self.SetTitle("Home")
elif self.listPanel.IsShown():
self.listPanel.Hide()
self.optPanel.Show()
self.SetTitle("Settings")
else:
self.SetTitle("Error")
self.Layout()
def Quit(self, e):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = bifr()
frame.Show()
app.MainLoop()
first off, i would highly suggest that you learn about wxpython sizers and get a good understanding of them (they're really not that hard the understand) as soon as possible before delving deeper into wxpython, just a friendly tip :).
as for your example, a few things:
when your'e not using sizers, you have to give size and position for every window or else they just wont show, so you'd have to change your panel classes to something like this (again this is only for demonstration, you should be doing this with wx.sizers, and not position and size):
class mListPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent,pos=(0,100),size=(500,500))
class settingsPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent,pos=(0,200),size (1000,1000))
further more, when binding an event it should look like this:
self.Bind(wx.EVT_TOOL, self.switchPanels, settingsToolButton)
self.Bind(wx.EVT_TOOL, self.switchPanels, homeToolButton)
notice how I've written only the name of the function without the added (), as an event is passed to it, you cant enter your own parameters to a function emitted from an event (unless you do it with the following syntax lambda e:FooEventHandler(paramaters))
and the event handler (function) should look like this:
def switchPanels(self, event):
if self.optPanel.IsShown():
self.optPanel.Hide()
self.listPanel.Show()
self.SetTitle("Home")
elif self.listPanel.IsShown():
self.listPanel.Hide()
self.optPanel.Show()
self.SetTitle("Settings")
else:
self.SetTitle("Error")
self.Layout()
there should always be a second parameter next to self in functions that are bind to event as the event object is passes there, and you can find its associated methods and parameters in the documentation (in this example it is the wx.EVT_TOOL).

TypeError: 'in <string>' requires string as left operand, not int with ObjectListView wxPython

When using ObjectListView in Python 2.7 - pressing any alphanumeric keystroke on the keyboard I get the following message error in my IDE (using PyCharm):
C:\Users\dylan_cissou\AppData\Local\Continuum\Anaconda\python.exe C:/Users/dylan_cissou/PycharmProjects/SPA/example.py
Traceback (most recent call last):
File "build\bdist.win-amd64\egg\ObjectListView\ObjectListView.py", line 1410, in _HandleChar
File "build\bdist.win-amd64\egg\ObjectListView\ObjectListView.py", line 1457, in _HandleTypingEvent
TypeError: 'in <string>' requires string as left operand, not int
What should I do to disable this message? I was trying to find where these two events were, so that I can override them but I was not able to find any.
Code example would be:
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)
#----------------------------------------------------------------------
def updateControl(self, event):
"""
"""
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"}
]
data = self.products + product_dict
self.dataOlv.SetObjects(data)
#----------------------------------------------------------------------
def setBooks(self, data=None):
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)
########################################################################
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()
Just press any keystroke, like '3', 'z' 'x' etc... you will the red error message each time.
Thanks you for your help.
Hhm, seems to be an issue with wxPython, I see the same in 2.8.12 Unicode build, 3.0.2 and Phoenix on Python 2.7 with OLV 1.3.2
evt.GetUnicodeKey should return a string as per Phoenix doc:
http://wxpython.org/Phoenix/docs/html/KeyEvent.html?highlight=getkeycode#KeyEvent.GetUnicodeKey
As per Robin Dunn this is incorrect, it should return an int.
I posted a question on wxPython-dev about this.
I will make a change to in olv._HandleTypingEvent, as follows::
if evt.GetUnicodeKey() == 0:
uniChar = chr(evt.GetKeyCode())
else:
uniChar = evt.GetUnicodeKey()
if uniChar not in string.printable:
return False
to:
if evt.GetUnicodeKey() == 0:
uniChar = chr(evt.GetKeyCode())
else:
uniChar = chr(evt.GetUnicodeKey())
if uniChar not in string.printable:
return False

Can't go back to wxpython main frame after creating and destroying Dialogue using ShowModal via Pubsub

I am programming an application using wxpython and wx.lib.pubsub. in python 2.7.3
1- There is a Frame with a menu item. When this menu is clicked, a message is published by pubsub.
2- This message destroys (if possible) and creates a "first level" dialogue.
3- "First Level" dialogue has an list of valules and an "add value" button. (NOTE: Such list of variables can be modified so I am trying to update this list)
4- When the "add value" button is clicked, another message is published by pubsub.
5- This message creates a "Second Level" dialogue, so a new name for the new variable can be written.
6- There is a "continue" button in this "second level" dialogue which has two consequences:
First one: Self.Destroy();
Second one: goes to step 2, i.e. destroys the "first level" dialogue and creates it again.
To that point the program seems to work fine, however, when I finish "adding" variables to the "first level" dialogue I Destroy it and then I cannot go back to the main Frame stated in step 1.
Why is this happening?
All the Dialogues are shown via ShowModal(). However if I use only Show() it seems to work fine but, since the program has many menus and items, ShowModal() is preferred.
Any idea why it works with Show() but not with ShowModal()?
If there is a simpler way to perform the task I want to do, it would be appreciated.
import wx
from wx.lib.pubsub import Publisher as pub
class itemReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'show.dialog')
def __OnShowDialog(self, message):
self.dlgParent = message.data[0]
print str(self.dlgParent)
self.valuesToShow = message.data[1]
print self.valuesToShow
#try to destroy dialog before creating a new one
try:
self.manageParametersDialog.Destroy()
except:
pass
self.manageParametersDialog = manageParamsDialog(self.dlgParent, self.valuesToShow)
print "ready to show first level dialogue"
self.manageParametersDialog.ShowModal() #if .Show() instead, there is no problem
class secondaryReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'add.item')
def __OnShowDialog(self, message):
dlgParent = message.data[0]
dlgGrandParent = message.data[1]
self.variableList = message.data[2]
editParameterDialog = editParamDlg(dlgParent, dlgGrandParent, self.variableList)
editParameterDialog.ShowModal()
class manageParamsDialog (wx.Dialog):
def __init__(self, parent, valueList):
self.valueList = valueList
self.parent = parent
wx.Dialog.__init__(self, parent, -1, "first level dialogue", style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
sizer=wx.BoxSizer(wx.VERTICAL)
self.optionList = wx.ListBox(self, -1, size=(200, 70), choices = valueList)
sizer.Add(self.optionList)
addButton = wx.Button(self, -1, 'Add New')
self.Bind(wx.EVT_BUTTON, self.OnButton, addButton)
sizer.Add(addButton)
cancelButton = wx.Button(self, -1, 'Cancel')
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelButton)
sizer.Add(cancelButton)
self.SetSizer(sizer)
self.Fit()
def OnButton (self, e):
pub.sendMessage('add.item', [self, self.parent, self.valueList])
def OnCancel(self,e):
self.Destroy()
class editParamDlg(wx.Dialog):
def __init__(self, parent, grandParent, variableList):
self.variableList = variableList
self.grandParent = grandParent
wx.Dialog.__init__(self, parent, -1, "second level dialogue", style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
hboxSizer = wx.BoxSizer(wx.HORIZONTAL)
self.textInput = wx.TextCtrl(self, -1)
hboxSizer.Add(self.textInput)
addButton = wx.Button(self, -1, 'Continue')
self.Bind(wx.EVT_BUTTON, self.OnAdd, addButton)
hboxSizer.Add(addButton)
cancelButton = wx.Button(self, -1, 'Cancel')
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelButton)
hboxSizer.Add(cancelButton)
self.SetSizer(hboxSizer)
self.Fit()
def OnAdd(self, e):
self.variableList.append(self.textInput.GetValue())
self.Destroy()
pub.sendMessage('show.dialog',[self.grandParent, self.variableList])
def OnCancel(self,e):
self.Destroy()
class ToolbarFrame(wx.Frame):
#this ToolbarFrame is the main window, with a Toolbar and a white panel below.
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, "this is a frame", size=(480, 320))
myPanel = wx.Panel(self)
myPanel.SetBackgroundColour("White")
menuBar = wx.MenuBar()
fileMenu = wx.Menu()
menuItem = wx.MenuItem(fileMenu, -1, "menu item", "opens dialog via pubsub")
self.Bind(wx.EVT_MENU, self.OnMenuItem, menuItem)
fileMenu.AppendItem(menuItem)
menuBar.Append(fileMenu, "File")
self.SetMenuBar(menuBar)
def OnMenuItem(self, e):
pub.sendMessage('show.dialog', [self, ["one", "two", "three"]])
app = wx.PySimpleApp()
frame = ToolbarFrame(parent=None, id=-1)
frame.Show()
newItemListener = itemReceiver()
editParameterListener = secondaryReceiver()
app.MainLoop()
try changing secondaryReciever as follows
class secondaryReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'add.item')
def __OnShowDialog(self, message):
dlgParent = message.data[0]
dlgGrandParent = message.data[1]
self.variableList = message.data[2]
editParameterDialog = editParamDlg(dlgParent, dlgGrandParent, self.variableList)
editParameterDialog.ShowModal()
#this line will not execute till the dialog closes
self.dlgParent.optionList.SetItems(editParameterDialog.variableList)
editParameterDialog.Destroy()
and also change editParamDlg
def OnAdd(self, e):
self.variableList.append(self.textInput.GetValue())
self.Close()
the problem was that you would call the show.modal from that OnAdd ... which would try to destroy the existing window and then open a new one... but the old one wasnt destroyed ... this left weird remnants that caused you errors ... and really all you want to do is update the item list ...

How to find an item in a panel

I am using Python WX to make a large GUI containing maybe 100 CheckBoxes. I want to read the value of each checkbox and append these values to a list. I can do this with 100 lines of code but prefer to use a loop. In the loop, how can I identify or select the specific checkbox I want to get the value from?
self.Box1 = wx.CheckBox(self.panel, id = 1, label='first box', pos=(10, 25), size=(30,22))
self.Box2 = wx.CheckBox(self.panel, id = 2, label='second box', pos=(20, 25), size=(30,22))
.
.
.
self.Box100 = wx.CheckBox(self.panel, id = 100, label='100th box', pos=(100, 25), size=(30,22))
Looking for something like:
MyList = []
for N in range (1, 101):
MyList.append(self.Box + N.Value)
The more generic question here is "how to select an object name in a loop"
I have searched all day with no luck. I am not a programming expert and hope this is worthy of someone's answer.
Rather than having 100 almost-identical lines of code, which is error-prone, inefficient and unattractive, actually build the CheckBoxes in a loop and hold them in a list:
self.boxes = []
for i in range(1, 101):
self.boxes.append(wx.CheckBox(self.panel, id=i,
label="Box {0}".format(i)
pos=(10, 25), size=(30,22)))
Then getting all of the values is similarly simple:
for i, box in enumerate(self.boxes, 1):
...
as is accessing a single one:
box = self.boxes[i-1]
If you really want "first", "second", "100th" write a helper function to process i into a string representation.
I personally like to use widget names. For example:
import wx
########################################################################
class MyPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
self.sizer = wx.BoxSizer(wx.VERTICAL)
for i in range(5):
txt = "Checkbox #%s" % i
chk = wx.CheckBox(self, label=txt, name=txt)
self.sizer.Add(chk, 0, wx.ALL|wx.CENTER, 5)
button = wx.Button(self, label="Get check")
button.Bind(wx.EVT_BUTTON, self.onButton)
self.sizer.Add(button, 0, wx.ALL|wx.CENTER, 5)
self.SetSizer(self.sizer)
#----------------------------------------------------------------------
def onButton(self, event):
""""""
widget = self.FindWindowByName("Checkbox #0")
print widget
print widget.GetValue()
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Checkboxes")
panel = MyPanel(self)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
This code will create a set of 5 wx.Checkboxes that each have a unique name. Then you can look them up by name using wx.FindWindowByName.
You could also create a dictionary using the names as the keys and the values as the CheckBox widgets which gives the advantage of being a faster lookup.

Categories

Resources