wx.Treectrl item look up table - python

I am trying to create a look up table to connect wxTreeItem to objects. Upon selecting or double clicking on the item an action should be taken on this object.
Mysteriously, I found that item instance returned after AppendItem is either a copy of the real item appended to the tree or self.tree.GetSelection() and event.GetItem() return a copy of the item in question.
import wx
class RandomObj(object):
def __init__(self, name):
self.name = name
class TreeExample(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Tree Example', size=(200, 130))
self.tree = wx.TreeCtrl(self, size=(200, 100))
root = self.tree.AddRoot('root')
self.itemLUT = {}
for obj in [RandomObj('item1'), RandomObj('item2'), RandomObj('item3')]:
item = self.tree.AppendItem(root, obj.name)
print item
self.itemLUT[id(item)] = obj
self.itemLUT[id(obj)] = item
self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivated, self.tree)
self.tree.Expand(root)
def OnActivated(self, event):
item = event.GetItem()
print 'Double clicked on', self.tree.GetItemText(item)
print id(item) in self.itemLUT.keys()
print self.tree.GetSelection()
print item
app = wx.PySimpleApp(None)
TreeExample().Show()
app.MainLoop()
Any suggestions? is there any proper way to connect and access an object upon an action (mouse or keyboard) on an tree item.

The best way is just to put your data into the item with SetItemData:
item = self.tree.AppendItem(root, obj.name)
self.tree.SetItemData(item,obj)
Then later, you can use GetItemData to extract the data back out of the item. You can put just about anything in there.

a good way to do it is
item = self.tree.AppendItem(root, obj.name)
self.tree.SetItemData(item, wx.TreeItemData(obj))
and in the event method
def OnActivated(self, event):
item = event.GetItem()
itemObject = self.tree.GetItemData(item).GetData()

Related

Retrieve the selected record

I have a QTableWidget with 9 columns and X rows. When I double-click a cell it displays its contents. How should I change the code to view, with a double-click, the entire row, i.e. the whole record?
class Searchtable(QTableWidget):
def __init__(self):
super().__init__()
self.tab = QTableWidget(0,9,self)
self.tab.setColumnWidth(8,130)
self.tab.setColumnWidth(7,70)
self.tab.setColumnWidth(6,70)
self.tab.setColumnWidth(5,130)
self.tab.setColumnWidth(4,50)
self.tab.setColumnWidth(3,60)
self.tab.setColumnWidth(2,100)
self.tab.setColumnWidth(1,130)
self.tab.setColumnWidth(0,130)
self.tab.verticalHeader().setVisible(False)
self.tab.horizontalHeader().setVisible(False)
self.tab.itemDoubleClicked.connect(self.doubleclick_Recordfound)
hbox1 = QHBoxLayout()
hbox1.addWidget(self.tab)
self.setLayout(hbox1)
def doubleclick_Recordfound(self):
print(self.tab.currentItem().text())
The signal itemDoubleClicked send the item pressed, from that item you can get the row so it's just a matter of iterating:
#pyqtSlot("QTableWidgetItem*")
def doubleclick_Recordfound(self, item):
r = item.row()
for c in range(self.tab.columnCount()):
it = self.tab.item(r, c)
if it is not None:
print(it.text())

Access sub interface values in the same function

I had two classes - main and sub interface
There is a pushbutton which will calls out the sub interface and I am trying to get the output of the sub tool interface directly (or almost immediately) so that it can be use within the push button function.
In my code, if I did the following:
hit on 'Click Me'
checked 2 options and hit the 'Apply to selected item' in the sub interface
the print line of 'my dict values' is still empty
Unless I create another function get_results in which then self.my_dict will be shown correctly.
As such, how can I code it in a way that once the 'Apply...' button is hit, self.my_dict will be updated without the need of creating another function? Or am I just overthinking things?
class SubMenuWindow(QtGui.QWidget):
def __init__(self, menu_items, parent=None):
super(SubMenuWindow, self).__init__(parent)
self.my_lyt = QtGui.QVBoxLayout()
self.checked_options = []
self.sel = {}
for menu_name, submenu_name in menu_items.items():
# Set the main menu item name
self.groupbox = QtGui.QGroupBox(self)
self.groupbox.setTitle(menu_name)
self.groupbox.setLayout(QtGui.QVBoxLayout())
self.my_lyt.addWidget(self.groupbox)
if submenu_name:
sub_txt = [action for action in submenu_name]
for s in sub_txt:
sub_chk = QtGui.QCheckBox(s)
self.checked_options.append(sub_chk)
self.groupbox.layout().addWidget(sub_chk)
apply_tag_btn = QtGui.QPushButton('Apply to selected item')
apply_tag_btn.clicked.connect(self.get_checked_options)
self.my_lyt.addWidget(apply_tag_btn)
self.my_lyt.addStretch()
self.setLayout(self.my_lyt)
self.show()
def get_checked_options(self):
for f in self.checked_options:
if f.isChecked():
self.sel[f.parent().title()] = f.text()
class MainWin(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWin, self).__init__(parent)
self.my_dict = {}
btnA = QtGui.QPushButton('Click Me')
btnA.clicked.connect(self.get_options)
btnB = QtGui.QPushButton('Get results')
btnB.clicked.connect(self.get_results)
layout = QtGui.QVBoxLayout()
layout.addWidget(btnA)
layout.addWidget(btnB)
self.setLayout(layout)
def get_options(self):
sample_dict = {'GrpA' : ['John', 'Zack'], 'GrpB' : ['Alice', 'Phan']}
self.subWin = SubMenuWindow(sample_dict)
# I had want to get the values from subWin as soon as User has hit on
# the 'Apply to selected item' button
self.my_dict = self.subWin.sel
print ">>> my dict values : ", self.my_dict
# do something else from here thereafter...
def get_results(self):
print self.subWin.sel
Creating the new window will not block, so your print statements will be executed before the user has anything selected. You could pass in a callback to notify the calling widget when the user changes the selection.
for example:
class SubMenuWindow(QtWidgets.QWidget):
def __init__(self, menu_items, parent=None, callback=None):
super(SubMenuWindow, self).__init__(parent)
self.callback = callback
[...]
def get_checked_options(self):
for f in self.checked_options:
if f.isChecked():
self.sel[f.parent().title()] = f.text()
if self.callback:
self.callback()
and pass in the callback:
def get_options(self):
sample_dict = {'GrpA' : ['John', 'Zack'], 'GrpB' : ['Alice', 'Phan']}
self.subWin = SubMenuWindow(sample_dict, callback=self.get_results)
[...]
this way your get_result method will be called whenever the user clicks the apply button in the SubMenuWindow.

How to tell where an item was dropped in qlistview?

I have a custom QListView:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from Diagnostics import Trace2 #writes to log file
import traceback
class ListOrderView(QListView):
itemMoved = pyqtSignal(int, int, QStandardItem) # Old index, new index, item
def __init__(self, parent=None):
try:
super(ListOrderView, self).__init__(parent)
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDefaultDropAction(Qt.MoveAction)
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.dragItem = None
self.dragRow = None
self.indexesMoved.connect(self.onIndexesMoved)
#self.installEventFilter(self)
except:
Trace2.WriteLine(str(traceback.format_exc()))
def onIndexesMoved(self, indexes):
Trace2.WriteLine("indexes were moved")
def dropEvent(self, event):
try:
super(ListOrderView, self).dropEvent(event)
self.selectionModel().setCurrentIndex(self.model().indexFromItem(self.dragItem), QItemSelectionModel.SelectCurrent)
Trace2.WriteLine("[LISTVIEW] item dropped")
Trace2.WriteLine("[LISTVIEW] current index is %d" %self.selectionModel().currentIndex().row())
Trace2.WriteLine("[LISTVIEW] current selection is %d" %self.selectionModel().selection().indexes()[0].row())
self.itemMoved.emit(self.dragRow, self.row(self.dragItem), self.dragItem)
self.dragItem = None
except:
Trace2.WriteLine(str(traceback.format_exc()))
def startDrag(self, supportedActions):
try:
self.dragItem = self.currentItem()
self.dragRow = self.row(self.dragItem)
super(ListOrderView, self).startDrag(Qt.MoveAction)
except:
Trace2.WriteLine(str(traceback.format_exc()))
def currentItem(self):
index = self.currentIndex()
item = self.model().itemFromIndex(index)
#Trace2.WriteLine("[LISTVIEW] currentItem = %s" % item.data(Qt.DisplayRole).toString())
return item
def row(self, item):
#index = self.model().indexFromItem(item)
index = self.selectedIndexes()[0]
row = index.row()
#Trace2.WriteLine("[LISTVIEW] row = %d" %row)
return row
And I really need to know where the item was dropped after a drag and drop operation so other things can be properly updated (I'm trying to put drag and drop into something never designed for it, big app, not my design). The selection model's current index and selection don't follow the dropped item, they stay behind effectively selecting a new item and screwing things up. Is there a way to make them move with the dropped item? The signal indexesMoved seems exactly like what I want, but it never fires. Am I using it wrong? Is there a different/better way?
I think you might need to actually have the model tell you where something was dropped, since it will ultimately handle the move:
class Model(QStandardItemModel):
rowDropped = pyqtSignal(int)
def dropMimeData(self, *args):
success = super(Model, self).dropMimeData(*args)
if success:
self.rowDropped.emit(args[2])
return success
This will emit, from the model, the row number on which the drop occurred. Your view already knows which item was dragged and dropped from its own events.
I am sure there are other ways in terms of tracking the object and then querying it again after the drop has completed.

Is it is possible to "set" a list to a ComboBox, wxpython?

Hi i know its possible to do this with lists however is it possible to do this with Comboboxes? Is there anything similar to the set function?
I have tried using set with a Combo box but i receive the following error:
AttributeError: 'ComboBox' object has no attribute 'Set'
Thanks.
Well, you can call SetItems(myList) to put a list into a ComboBox, overwriting what's already in it.
EDIT: The most common method to create a list in a combobox's list is like this:
myList = ["dog", "cat", "hamster"]
cbo = wx.ComboBox(self, choices=myList)
But since ComboBox inherits from ItemContainer, you can also do it like this complete example:
import wx
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Test")
panel = wx.Panel(self)
myList = ["dog", "cat", "hamster"]
cbo = wx.ComboBox(panel)
cbo.SetItems(myList)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
http://www.wxpython.org/docs/api/wx.ComboBox-class.html
__init__(parent, id, value, pos, size, choices, style, validator, name)
combobox = wx.ComboBox(self, choices=myList)
I believe your are asking for a method to add new items "at runtime"? ie after the form is created? See the code below if so ;-)
def UpdateCitiesCombo(self):
self.cmbCities.Clear()
pc = PostalCode()
if self.txtPostalCode.Value:
cities = pc.GetFromCode(int(self.txtPostalCode.Value))
for city in cities:
self.cmbCities.Append(city[2])
items = self.cmbCities.GetItems()
index = -1
try:
if self.customer.city != "":
index = items.index(self.customer.city)
else:
index = 0
self.cmbCities.SetSelection(index)
except ValueError:
self.cmbCities.SetValue(self.customer.city)
In essence what you should not is the Clear() and Append() methods of the ComboBox and the fact that this function is called from an event somewhere. Hope it is what you are looking for.

wxPython ListCtrl Column Ignores Specific Fields

I'm rewriting this post to clarify some things and provide a full class definition for the Virtual List I'm having trouble with. The class is defined like so:
from wx import ListCtrl, LC_REPORT, LC_VIRTUAL, LC_HRULES, LC_VRULES, \
EVT_LIST_COL_CLICK, EVT_LIST_CACHE_HINT, EVT_LIST_COL_RIGHT_CLICK, \
ImageList, IMAGE_LIST_SMALL, Menu, MenuItem, NewId, ITEM_CHECK, Frame, \
EVT_MENU
class VirtualList(ListCtrl):
def __init__(self, parent, datasource = None,
style = LC_REPORT | LC_VIRTUAL | LC_HRULES | LC_VRULES):
ListCtrl.__init__(self, parent, style = style)
self.columns = []
self.il = ImageList(16, 16)
self.Bind(EVT_LIST_CACHE_HINT, self.CheckCache)
self.Bind(EVT_LIST_COL_CLICK, self.OnSort)
if datasource is not None:
self.datasource = datasource
self.Bind(EVT_LIST_COL_RIGHT_CLICK, self.ShowAvailableColumns)
self.datasource.list = self
self.Populate()
def SetDatasource(self, datasource):
self.datasource = datasource
def CheckCache(self, event):
self.datasource.UpdateCache(event.GetCacheFrom(), event.GetCacheTo())
def OnGetItemText(self, item, col):
return self.datasource.GetItem(item, self.columns[col])
def OnGetItemImage(self, item):
return self.datasource.GetImg(item)
def OnSort(self, event):
self.datasource.SortByColumn(self.columns[event.Column])
self.Refresh()
def UpdateCount(self):
self.SetItemCount(self.datasource.GetCount())
def Populate(self):
self.UpdateCount()
self.datasource.MakeImgList(self.il)
self.SetImageList(self.il, IMAGE_LIST_SMALL)
self.ShowColumns()
def ShowColumns(self):
for col, (text, visible) in enumerate(self.datasource.GetColumnHeaders()):
if visible:
self.columns.append(text)
self.InsertColumn(col, text, width = -2)
def Filter(self, filter):
self.datasource.Filter(filter)
self.UpdateCount()
self.Refresh()
def ShowAvailableColumns(self, evt):
colMenu = Menu()
self.id2item = {}
for idx, (text, visible) in enumerate(self.datasource.columns):
id = NewId()
self.id2item[id] = (idx, visible, text)
item = MenuItem(colMenu, id, text, kind = ITEM_CHECK)
colMenu.AppendItem(item)
EVT_MENU(colMenu, id, self.ColumnToggle)
item.Check(visible)
Frame(self, -1).PopupMenu(colMenu)
colMenu.Destroy()
def ColumnToggle(self, evt):
toggled = self.id2item[evt.GetId()]
if toggled[1]:
idx = self.columns.index(toggled[2])
self.datasource.columns[toggled[0]] = (self.datasource.columns[toggled[0]][0], False)
self.DeleteColumn(idx)
self.columns.pop(idx)
else:
self.datasource.columns[toggled[0]] = (self.datasource.columns[toggled[0]][0], True)
idx = self.datasource.GetColumnHeaders().index((toggled[2], True))
self.columns.insert(idx, toggled[2])
self.InsertColumn(idx, toggled[2], width = -2)
self.datasource.SaveColumns()
I've added functions that allow for Column Toggling which facilitate my description of the issue I'm encountering. On the 3rd instance of this class in my application the Column at Index 1 will not display String values. Integer values are displayed properly. If I add print statements to my OnGetItemText method the values show up in my console properly. This behavior is not present in the first two instances of this class, and my class does not contain any type checking code with respect to value display.
It was suggested by someone on the wxPython users' group that I create a standalone sample that demonstrates this issue if I can. I'm working on that, but have not yet had time to create a sample that does not rely on database access. Any suggestions or advice would be most appreciated. I'm tearing my hair out on this one.
Are you building on the wxPython demo code for virtual list controls? There are a couple of bookkeeping things you need to do, like set the ItemCount property.
One comment about your OnGetItemText method: Since there's no other return statement, it will return None if data is None, so your test has no effect.
How about return data or "" instead?
There's a problem with the native object in Windows. If GetImg returns None instead of -1 the list has a problem with column 1 for some reason. That from Robin over on the Google Group post for this issue.

Categories

Resources