I'd actually like to start by admitting that I'm terrified to ask this question. That said, I have the following combination of classes:
A Dialog Class:
class formDialog(wx.Dialog):
def __init__(self, parent, id = -1, panel = None, title = _("Unnamed Dialog"),
modal = False, sizes = (400, -1)):
wx.Dialog.__init__(self, parent, id, _(title),
style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
if panel is not None:
self._panel = panel(self)
self._panel.SetSizeHints(*sizes)
ds = wx.GridBagSizer(self._panel._gap, self._panel._gap)
ds.Add(self._panel, (0, 0), (1, 1), wx.EXPAND | wx.ALL, self._panel._gap)
ds.Add(wx.StaticLine(self), (1, 0), (1, 1), wx.EXPAND | wx.RIGHT | wx.LEFT, self._panel._gap)
self.bs = self.CreateButtonSizer(self._panel._form['Buttons'])
ds.Add(self.bs, (2, 0), (1, 1), wx.ALIGN_RIGHT | wx.ALL, self._panel._gap)
ds.AddGrowableCol(0)
ds.AddGrowableRow(0)
self.SetSizerAndFit(ds)
self.Center()
self.Bind(wx.EVT_BUTTON, self._panel.onOk, id = wx.ID_OK)
self.Bind(wx.EVT_BUTTON, self._panel.onClose, id = wx.ID_CANCEL)
self.Bind(wx.EVT_CLOSE, self._panel.onClose)
if modal:
self.ShowModal()
else:
self.Show()
A Form Class:
class Form(wx.Panel):
reqFields = [
('Defaults', {}),
('Disabled', [])
]
def __init__(self, parent = None, id = -1, gap = 2, sizes = (-1, -1)):
wx.Panel.__init__(self, parent, id)
self.SetSizeHints(*sizes)
self._gap = gap
self.itemMap = {}
if hasattr(self, '_form'):
# There are a number of fields which need to exist in the form
# dictionary. Set them to defaults if they don't exist already.
for k, d in self.reqFields:
if not self._form.has_key(k):
self._form[k] = d
self._build()
def _build(self):
"""
The Build Method automates sizer creation and element placement by parsing
a properly constructed object.
"""
# The Main Sizer for the Panel.
panelSizer = wx.GridBagSizer(self._gap, self._gap)
# Parts is an Ordered Dictionary of regions for the form.
for group, (key, data) in enumerate(self._form['Parts'].iteritems()):
flags, sep, display = key.rpartition('-') ##UnusedVariable
# HR signifies a Horizontal Rule for spacing / layout. No Data Field.
if display == 'HR':
element = wx.StaticLine(self)
style = wx.EXPAND
# Any other value contains elements that need to be placed.
else:
element = wx.Panel(self, -1)
# The Row Sizer
rowSizer = wx.GridBagSizer(self._gap, self._gap)
for row, field in enumerate(data):
for col, item in enumerate(field):
style = wx.EXPAND | wx.ALL
pieces = item.split('-')
# b for Buttons
if pieces[0] == 'b':
control = wx._controls.Button(element, -1, pieces[1])
# custom items - Retrieve from the _form object
if pieces[0] == 'custom':
control = self._form[pieces[1]](element)
# The row in the Grid needs to resize for Lists.
panelSizer.AddGrowableRow(group)
# Now the Row has to grow with the List as well.
rowSizer.AddGrowableRow(row)
# custom2 - Same as custom, but does not expand
if pieces[0] == 'custom2':
control = self._form[pieces[1]](element)
style = wx.ALL
# c for CheckBox
if pieces[0] == 'c':
control = wx.CheckBox(element, label = _(pieces[2]), name = pieces[1])
control.SetValue(int(self._form['Defaults'].get(pieces[1], 0)))
# d for Directory Picker
if pieces[0] == 'd':
control = wx.DirPickerCtrl(element, name = pieces[1])
control.GetTextCtrl().SetEditable(False)
control.GetTextCtrl().SetName(pieces[1])
control.GetTextCtrl().SetValue(self._form['Defaults'].get(pieces[1], ''))
# f for File Browser
if pieces[0] == 'f':
control = wx.FilePickerCtrl(element, name = pieces[1], wildcard = pieces[2])
control.GetTextCtrl().SetEditable(False)
control.GetTextCtrl().SetValue(self._form['Defaults'].get(pieces[1], ''))
# f2 for Save File
if pieces[0] == 'f2':
control = wx.FilePickerCtrl(element, name = pieces[1],
style = wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT | wx.FLP_USE_TEXTCTRL,
wildcard = pieces[2])
control.GetTextCtrl().SetEditable(False)
# h for Horizontal Rule - layout helper.
if pieces[0] == 'h':
control = wx.StaticLine(element)
style = wx.EXPAND
# l for Label (StaticText)
if pieces[0] == 'l':
control = wx.StaticText(element, label = _(pieces[1]))
# Labels do not expand - override default style.
style = wx.ALL | wx.ALIGN_CENTER_VERTICAL
# p for Password (TextCtrl with Style)
if pieces[0] == 'p':
control = wx.TextCtrl(element, name = pieces[1], style = wx.TE_PASSWORD)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# s for ComboBox (Select)
if pieces[0] == 's':
control = wx.ComboBox(element, name = pieces[1],
choices = self._form['Options'].get(pieces[1], []),
style = wx.CB_READONLY)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# s2 for Spin Control
if pieces[0] == 's2':
control = wx.SpinCtrl(element, name = pieces[1], size = (55, -1),
min = int(pieces[2]), max = int(pieces[3]))
control.SetValue(int(self._form['Defaults'].get(pieces[1], 1)))
# Spin Ctrl's do not expand.
style = wx.ALL
# t for TextCtrl
if pieces[0] == 't':
control = wx.TextCtrl(element, name = pieces[1])
try:
control.SetValidator(self._form['Validators'][pieces[1]])
except KeyError: pass # No Validator Specified.
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# tr for Readonly TextCtrl
if pieces[0] == 'tr':
control = wx.TextCtrl(element, name = pieces[1], style = wx.TE_READONLY)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# Check for elements disabled by default. Store reference to
# Element in itemMap for reference by other objects later.
if len(pieces) > 1:
if pieces[1] in self._form['Disabled']:
control.Enable(False)
self.itemMap[pieces[1]] = control
# Place the control in the row.
rowSizer.Add(control, (row, col), (1, 1), style, self._gap)
if style == wx.EXPAND | wx.ALL:
rowSizer.AddGrowableCol(col)
if 'NC' not in flags:
sb = wx.StaticBox(element, -1, _(display))
sz = wx.StaticBoxSizer(sb, wx.VERTICAL)
sz.Add(rowSizer, 1, flag = wx.EXPAND)
element.SetSizerAndFit(sz)
else:
element.SetSizerAndFit(rowSizer)
panelSizer.Add(element, (group, 0), (1, 1), wx.EXPAND | wx.ALL, self._gap)
panelSizer.AddGrowableCol(0)
self.SetSizerAndFit(panelSizer)
def getDescendants(self, elem, list):
children = elem.GetChildren()
list.extend(children)
for child in children:
self.getDescendants(child, list)
def getFields(self):
fields = []
self.getDescendants(self, fields)
# This removes children we can't retrieve values from. This should result
# in a list that only contains form fields, removing all container elements.
fields = filter(lambda x: hasattr(x, 'GetValue'), fields)
return fields
def onOk(self, evt):
self.onClose(evt)
def onClose(self, evt):
self.GetParent().Destroy()
The Form is meant to be used by subclassing like so:
class createQueue(Form):
def __init__(self, parent):
self._form = {
'Parts' : OrderedDict([
('Queue Name', [
('t-Queue Name',)
])
]),
'Buttons' : wx.OK | wx.CANCEL
}
Form.__init__(self, parent)
class generalSettings(Form):
def __init__(self, parent):
self._form = {
'Parts': OrderedDict([
('Log Settings', [
('l-Remove log messages older than: ', 's2-interval-1-10', 's-unit')
]),
('Folder Settings', [
('l-Spool Folder Location:', 'd-dir'),
('l-Temp Folder Location:', 'd-temp')
]),
('Email Notifications', [
('l-Alert Email To:', 't-alert_to'),
('l-Alert Email From:', 't-alert_from'),
('l-Status Email From:', 't-status_from'),
('l-Alert Email Server:', 't-alert_host'),
('l-Login:', 't-alert_login'),
('l-Password:', 'p-alert_password')
]),
('Admin User', [
('c-req_admin-Require Admin Rights to make changes.',)
]),
('Miscellaneous', [
('l-Print Worker Tasks:', 's2-printtasks-1-256', 'l-Job Drag Options:', 's-jobdrop')
])
]),
'Options': {
'unit': ['Hours', 'Days', 'Months'],
'jobdrop': ['Move Job to Queue', 'Copy Job to Queue']
},
'Buttons': wx.OK | wx.CANCEL
}
Form.__init__(self, parent)
These might be used like so:
formDialog(parent, panel = createQueue, title = 'Create a Queue', sizes = (200, -1))
formDialog(parent, panel = generalSettings, title = "General Settings")
Whew, that's a ton, and thanks to anyone who makes it down this far. The idea is that I want something that takes care of the monotonous parts of layout in wxPython. I am designing a user interface that will need to create 100's of different Dialogs and Forms. I wanted something that would allow me to generate the forms dynamically from a structured object.
I'd like to hear other developers' thoughts on this kind of approach. The closest I've seen to something similar is Drupal's Form API. I feel like it is viable for these reasons:
Easily rearrange fields.
No need to create / manage Sizers manually.
Compound / complex forms can be created easily.
Display helper elements (StaticBoxSizers, Static Lines) are easily added.
I am concerned that it is an undesirable approach for these reasons:
Long _build() function body in the Form Class.
May not be clear to other developers at first glance.
Uses Structured Strings to define fields.
There may be a better way.
Any thoughts, constructive, destructive, or otherwise will all be appreciated. Thanks!
You should also try wxFormDesigner or XRCed.
Since you're using wx, you should study wxglade. It's a graphical GUI builder which you use to build your GUI and it generates a .wxg file with the layout, and you can load that into your script.
The file is actually just xml, so you can programatically generate it and dynamically load different GUIs from it. Maybe that helps.
Related
I'm receiving the following warnings in my GTK 3 application:
Gtk-WARNING **: Allocating size to __main__+MCVEWindow 0000000004e93b30 without calling gtk_widget_get_preferred_width/height(). How does the code know the size to allocate?
The warnings occurs when Gtk.ScrolledWindow containing Gtk.TreeView is attached to the grid, the grid itself is attached to the gtk.ApplicationWindow and there are enough elements for the scrollbar to actually appear. If there aren't enough elements to make it scrollable, the warning doesn't appear.
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk as gtk
class MCVEWindow(gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._tree_view = gtk.TreeView()
self._tree_view.set_hexpand(True)
self._tree_view.set_vexpand(True)
self.populate_tree_view() # populate tree view with fake items
window_column = gtk.TreeViewColumn(
"Window", gtk.CellRendererText(),
text=0
)
window_column.set_resizable(True)
handle_column = gtk.TreeViewColumn(
"Handle", gtk.CellRendererText(),
text=1
)
class_column = gtk.TreeViewColumn(
"Class name", gtk.CellRendererText(),
text=2
)
self._tree_view.append_column(window_column)
self._tree_view.append_column(handle_column)
self._tree_view.append_column(class_column)
scrolled_tree_view = gtk.ScrolledWindow()
scrolled_tree_view.add(self._tree_view)
toolbar = gtk.Toolbar()
expand_tree_view_button = gtk.ToolButton(icon_name="list-add")
expand_tree_view_button.connect(
"clicked",
lambda e: self._tree_view.expand_all()
)
collapse_tree_view_button = gtk.ToolButton(icon_name="list-remove")
collapse_tree_view_button.connect(
"clicked",
lambda e: self._tree_view.collapse_all()
)
toolbar.insert(expand_tree_view_button, -1)
toolbar.insert(collapse_tree_view_button, -1)
status_bar = gtk.Statusbar()
status_bar.push(
status_bar.get_context_id("Status message"),
"A status message."
)
self._master_grid = gtk.Grid()
self._master_grid.attach(toolbar, 0, 0, 1, 1)
self._master_grid.attach(scrolled_tree_view, 0, 1, 1, 1)
self._master_grid.attach(status_bar, 0, 2, 1, 1)
self.add(self._master_grid)
self.connect("delete-event", gtk.main_quit)
self.show_all()
def populate_tree_view(self):
tree_store = gtk.TreeStore(str, str, str)
# Warnings don't occur when there are less than 100 "root" items
for i in range(100):
item1 = tree_store.append(
None,
["Window " + str(i + 1), "12345678", "ClassName"]
)
for j in range(3):
item2 = tree_store.append(
item1,
["Window " + str(i + 1) + str(i + 2),
"12345678",
"ClassName"]
)
for k in range(5):
tree_store.append(
item2,
["Window " + str(i + 1) + str(j + 1) + str(k + 1),
"12345678",
"ClassName"]
)
self._tree_view.set_model(tree_store)
class MCVEApp(gtk.Application):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def do_activate(self):
MCVEWindow()
gtk.main()
if __name__ == "__main__":
MCVEApp().run()
You should be able to copy, paste and run this code if you have environment set up.
The warnings don't follow any specific pattern, sometimes there is one warning, sometimes two or more. The warrnings also pop up whenever I expand all tree items.
GTK version is 3.22.18
What could cause these warnings?
I received the answer on the GTK App dev mailing list which lead me to the solution:
Attaching TreeView to GTK Grid which is then added to the ScrolledWindow solved the issue for me.
Instead of this
scrolled_tree_view = gtk.ScrolledWindow()
scrolled_tree_view.add(self._tree_view)
you need to do the following
scrolled_tree_view = gtk.ScrolledWindow()
grid = gtk.Grid()
grid.attach(self._tree_view, 0, 0, 1, 1)
scrolled_tree_view.add(grid)
Unfortunately, this isn't documented anywhere.
I am trying to filter an objectlistview based on a text value in a certain column of my data. I can't figure out how to construct the filter properly. I have been refering to:
http://objectlistview.sourceforge.net/python/features.html
http://objectlistview.sourceforge.net/python/majorClasses.html
I am able to get Filter.Head(n) to work sucessfully but not ableto get Filter.TextSearch(objectListView, columns=(), text="") to work because I don't understand what the parameter types are suppost to be.
I have written the below code:
def OnFilterMeter(self,event):
list = self.exception_panel.col_list ## The lenght of my column list
col = list[len(list)-1] ## I want to search the last column
meter_flt = Filter.TextSearch(self.exception_panel,columns = (col), text = "10") ## Create filter to find string 10 in last column
self.exception_panel.SetFilter(meter_flt)
self.exception_panel.populate()
I can't understand why this doesn't work. The program doesn't filter the list when it tries to populate the list view again. At the very least it should show all blanks because it cannot find any items that are equal to "10". Please help. Thank you
Below is my code :
class Display_Manager(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, -1, title)
self.exception_panel = Exception_Display(self,0)
# self.exception_panel2 = Exception_Display(self,1)
## Setup FileMenu Bar Setup
filemenu= wx.Menu() ## Create Menu Bar
filemenu.Append(ID_LOG,"Login","Login as user with password.") ## Clicking shoudl load a single dailyi report which user selects
filemenu.Append(ID_LOAD,"Load Report","Load Daliy Exception Report") ## Clicking shoudl load a single dailyi report which user selects
filemenu.Append(ID_LOADS,"Load All","Load All Daliy Exception Reports") ## Clicking should load all daily reports in a directory which user selects
filemenu.Append(ID_REFRESH,"Refresh DB","Refresh Database. Overwrites any un saved data.") ## Clicking should refreseh the database and overwrite any unsaved changes
filemenu.Append(ID_SAVE,"Save DB","Saves and commits any changes to database.") ## Clicking will save and commit any changes or new data to the database
filemenu.Append(ID_EXIT,"E&xit"," Terminate the program") ## exit the program
## Setup Edit Menu Bar
editmenu = wx.Menu()
editmenu.Append(ID_ADD,"Add","Add Exception")
editmenu.Append(ID_DELETE,"Delete","Delete Exception")
editmenu.Append(ID_UNDO,"Stepback DB","Go back to a previous version of the database")
editmenu.Append(ID_REDO,"Stepfoward DB","Go foward to a previous version of the database")
## Setup Report Menu
reportmenu = wx.Menu()
reportmenu.Append(ID_REPORT,"Exception Report","Generate an Excel Report of selected exceptions.")
reportmenu.Append(ID_REPORT2,"Meter Report","Generate an Excel Report of selected exceptions.")
reportmenu.Append(ID_REPORT3,"Transformer Report","Generate an Excel Report of selected exceptions.")
## Setup Bucket Menu
bucketmenu = wx.Menu()
bucketmenu.Append(ID_METERPROB,"Meter Problem","Place the meter in the meter problem bucket.")
bucketmenu.Append(ID_LOWVOLT,"Investigate Low Voltage","Place the meter in the investigate bucket.")
bucketmenu.Append(ID_TURNS,"Turns Ratio","Place the meter in the turns ratio bucket.")
## Setup Configure Menu Menu
configmenu = wx.Menu()
configmenu.Append(ID_FILTER,"Set Filters","Filter by district and company.")
configmenu.Append(ID_SAVEFIL,"Save Filters","Save Filters for Later use.")
configmenu.Append(ID_LOADFIL,"Load Filters","Load Filters for Later use.")
## Add file menu bar
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File")
menuBar.Append(editmenu, "&Edit")
menuBar.Append(reportmenu, "&Reports")
menuBar.Append(configmenu, "&Config")
menuBar.Append(bucketmenu, "&Bucket")
self.SetMenuBar(menuBar)
## Create Toolbar
tb = self.CreateToolBar( wx.TB_HORIZONTAL | wx.NO_BORDER |
wx.TB_FLAT | wx.TB_TEXT)
tb.AddSimpleTool(10, wx.Bitmap('images/database.png'), 'Save Database')
tb.AddSimpleTool(20, wx.Bitmap('images/excel.png'), 'Get Quick Excel Report')
tb.AddSimpleTool(70, wx.Bitmap('images/analyze.png'), 'Analyze Selected Exceptions')
tb.AddSimpleTool(71, wx.Bitmap('images/refresh.png'), 'Refresh Selected Relationships')
tb.AddSeparator()
tb.AddSimpleTool(30, wx.Bitmap('images/today.png'), 'Filter only the latest data.')
tb.AddSimpleTool(40, wx.Bitmap('images/all_time.png'), 'Filter All Data from all time')
tb.AddSimpleTool(41, wx.Bitmap('images/date_range.png'), 'Select a date range to view data over')
tb.AddSeparator()
tb.AddSimpleTool(50, wx.Bitmap('images/AMI_Meter.png'), 'Bucket as meter problem')
tb.AddSimpleTool(60, wx.Bitmap('images/violation.png'), 'Bucket to District')
tb.AddSimpleTool(61, wx.Bitmap('images/turns.png'), 'Bucket Turns Ratio')
tb.AddSimpleTool(62, wx.Bitmap('images/plan.png'), 'Bucket for further review for planning engineer')
tb.AddSeparator()
tb.AddSimpleTool(63, wx.Bitmap('images/cyme.png'), 'Load CYME model for specific meter')
tb.AddSimpleTool(64, wx.Bitmap('images/relate_dot.png'), 'Cluster & Relate Meter Exceptions')
tb.AddSeparator()
tb.AddSimpleTool(65, wx.Bitmap('images/bucket_m.png'), 'Filter Meter Bucket')
tb.AddSimpleTool(66, wx.Bitmap('images/bucket_v.png'), 'Filter Violation Bucket')
tb.AddSimpleTool(67, wx.Bitmap('images/bucket_t.png'), 'Filter Turns Ratio Bucket')
tb.AddSimpleTool(68, wx.Bitmap('images/bucket_p.png'), 'Filter Planning Bucket')
tb.SetToolBitmapSize((84,84))
tb.Realize()
self.Bind(wx.EVT_TOOL,self.OnRefresh,id =71)
self.Bind(wx.EVT_TOOL,self.OnAnalyze,id =70)
self.Bind(wx.EVT_TOOL,self.OnSave,id =10)
self.Bind(wx.EVT_TOOL,self.OnBucketMeter,id =50)
self.Bind(wx.EVT_TOOL,self.OnVioMeter,id =60)
self.Bind(wx.EVT_TOOL,self.OnTurnsMeter,id =61)
self.Bind(wx.EVT_TOOL,self.OnPlanMeter,id =62)
self.Bind(wx.EVT_TOOL,self.OnFilterMeter,id =65)
# self.Bind(wx.EVT_TOOL,self.OnFilterMeter,id =66)
# self.Bind(wx.EVT_TOOL,self.OnFilterMeter,id =67)
# self.Bind(wx.EVT_TOOL,self.OnFilterMeter,id =68)
## Create Sizers
# self.main_sizer = wx.BoxSizer(wx.VERTICAL)
# self.top_sizer = wx.BoxSizer(wx.HORIZONTAL)
# self.bottom_sizer = wx.BoxSizer(wx.HORIZONTAL)
# self.main_sizer.Add(self.top_sizer,0,wx.EXPAND)
# self.main_sizer.Add(self.bottom_sizer,0,wx.EXPAND)
## Show the frame
# self.SetSizer(self.main_sizer)
self.Center()
self.Show(True)
def OnSave(self,event):
session.commit()
print "ON Save"
def OnRefresh(self,event):
self.exception_panel.populate()
self.ColorBuckets()
print "OnRelate"
def OnAnalyze(self,event):
objectList = self.exception_panel.GetSelectedObjects() ## Get list of selected objects to relate in the database
for object in objectList:
print object
object.calculate()
# print self.GetValueAt(self.GetObjectAt(event.rowIndex),event.subItemIndex)
# self.temp_value = event.editor.GetValue()
self.exception_panel.populate()
print "OnAnalze"
def OnFilterDate1(self,event):
print "on Filter date"
def OnFilterDate2(self,event):
print "on Filter date"
def OnFilterDate3(self,event):
print "on Filter date"
def OnFilterMeter(self,event):
list = self.exception_panel.col_list
col = list[len(list)-1]
# meter_flt = Filter.Head(10)
meter_flt = Filter.TextSearch(self.exception_panel,columns = (col), text = "10")
self.exception_panel.SetFilter(meter_flt)
self.exception_panel.populate()
# self.exception_panel.Refresh()
# self.exception_panel.populate()
# self.exception_panel.SetObjects(qrty_meters_excpt)
# self.exception_panel.populate()
print "On Filter Meter"
def OnBucketMeter(self,event):
objectList = self.exception_panel.GetSelectedObjects()
for object in objectList:
print object
object.known_flags = 20 ## Meter Mismatch Flag Known ***ffa500
self.exception_panel.populate()
self.ColorBuckets()
def OnVioMeter(self,event):
objectList = self.exception_panel.GetSelectedObjects()
for object in objectList:
object.known_flags = 10 ## Meter Mismatch Flag Known ***ffa500
self.exception_panel.populate()
self.ColorBuckets()
def OnTurnsMeter(self,event):
objectList = self.exception_panel.GetSelectedObjects()
for object in objectList:
object.known_flags = 30 ## Meter Mismatch Flag Known ***ffa500
self.exception_panel.populate()
self.ColorBuckets()
def OnPlanMeter(self,event):
objectList = self.exception_panel.GetSelectedObjects()
for object in objectList:
object.known_flags = 40 ## Meter Mismatch Flag Known ***ffa500
self.exception_panel.populate()
self.ColorBuckets()
def ColorBuckets(self): ## Color All Buckets according to knowflags
## Query the session for only items that have know_flags greate than 0
qrty_color_buckets = session.query(Exception).filter(Exception.known_flags != "").all()
print qrty_color_buckets
for exception in qrty_color_buckets:
print exception.known_flags == 10
flag = int(exception.known_flags) ## alias the flag
if flag == 20: ## Meter Mismatch
self.exception_panel.SetItemBackgroundColour(self.exception_panel.GetIndexOf(exception),'#ffa500') ## Oranage
elif flag == 10: ## Violation
self.exception_panel.SetItemBackgroundColour(self.exception_panel.GetIndexOf(exception),'#ff0000') ## Red
elif flag == 30: ## Turns Ratio
self.exception_panel.SetItemBackgroundColour(self.exception_panel.GetIndexOf(exception),'#0066CC') ## Blue
elif flag == 40: ## Plan referal
self.exception_panel.SetItemBackgroundColour(self.exception_panel.GetIndexOf(exception),'#FFFF00') ## Yellow
Please see the function OnFilterMeter.
The following works for me, it does a search on the second column and it shows just one matching entry.
# -*- coding: utf-8 -*-
import datetime
import wx
import wx.lib.sized_controls as SC
import ObjectListView as OLV
class MyData:
def __init__(self, year, month, day, level, sets, reps):
self.date = datetime.date(year, month, day)
self.level = level
self.sets = sets
self.reps = reps
def GetDate(self):
return self.date
def GetLevel(self):
return self.level
def GetSets(self):
return self.sets
def GetRepetitions(self):
return self.reps
class MyListCtrl(OLV.GroupListView):
def __init__(self, parent):
super(MyListCtrl, self).__init__(parent, wx.ID_ANY, style=wx.LC_REPORT)
self.SetColumns(self._ColumnDefinitions())
meter_flt = OLV.Filter.TextSearch(self, columns=self.columns[2:3],
text="7")
self.SetFilter(meter_flt)
self.SetObjects(self._DataObjects())
def _DataObjects(self):
return [MyData(2010,10,8, 1, 2, 3),
MyData(2005,10,10, 7, 2, 3),
MyData(2010,10,3, 2, 2, 3),
MyData(2012,10,10, 1, 2, 3),
MyData(2014,10,10, 1, 2, 3)
]
def _ColumnDefinitions(self):
return [OLV.ColumnDefn('Date', valueGetter='GetDate', groupKeyGetter='GetDate'),
OLV.ColumnDefn('Level', valueGetter='GetLevel', width=150),
OLV.ColumnDefn('Sets', valueGetter='GetSets', width=100, groupKeyGetter='GetSets'),
OLV.ColumnDefn('Reps', valueGetter='GetRepetitions', width=200)]
class MyFrame(SC.SizedFrame):
def __init__(self):
super(MyFrame, self).__init__(None)
pane = self.GetContentsPane()
olv = MyListCtrl(pane)
olv.SetSizerProps(expand=True, proportion=1)
if __name__ == '__main__':
import wx.lib.mixins.inspection as WIT
app = WIT.InspectableApp()
win = MyFrame()
win.Show()
app.MainLoop()
I have a problem when I do click on the listbox, In my program, I did click on the listbox will appear a video and explain about the video. I made a sample in the database are "name_link" which will appear in the listbox. expl1, expl2, expl3. Of each name_link have different information. However, that happens every time I click on one of these name_link, video that appears only Video3 and system never show the explain about video. When I click name_link video1 video2 emerging or always Video3. I am so stuck in this section.
this section during click :
tb4 = wx.StaticText(self, -1, label='explain', pos=(20, 145))
wx.TextCtrl(self, -1, pos=(80, 145), size=(220, 120))
self.opt = wx.ListBox(pan1, -1, pos=(10, 210), size=(480, 250), style= wx.TE_MULTILINE | wx.BORDER_SUNKEN)
def playFile(self, event):
self.player.Play()
def OnEnter(self, event):
self.opt.SetLabel(self.PatMatch())
def PatMatch(self):
con = sqlite3.connect('test.db')
with con:
cur = con.cursor()
for row in cur.execute("Select * From Video"):
klmt = self.inpt.GetValue()
if row[1] in klmt.lower():
self.opt.Append(row[2])
self.player.Load(row[4])
return self.Bind(wx.EVT_LISTBOX_DCLICK, self.playFile, self.op)
The data base like this :
id word name_link explain link
--- ------ ----------- --------- --------
1 python Video1 test C:\Users\Ihsan\Downloads\Video1.MP4
2 python Video2 test1 C:\Users\Ihsan\Downloads\Video2.MP4
3 python Video3 test2 C:\Users\Ihsan\Downloads\Video3.MP4
There are several issues:
You want bind wx.EVT_LISTBOX_DCLICK only once. When the event is fired, you want to read out which item (GetSelection) was selected when double-clicking. Use GetSelections for multiple selections.
Bad indentation: In the inner loop (self.player…) and with the return self.Bind… which should be in the innermost loop. As it is written now it will bind once to the last element. As written in point 1 this is not the way how it is done anyhow.
In Bind, self.op should be self.opt
See the wxPython demo on the download page to see how to use ListBox sensibly.
EDIT: Code sample added
import wx
import sqlite3
class play_model(object):
def __init__(self):
# all the sqlite init stuff goes here, it is mocked for the sake of the example
self.conn = sqlite3.connect(':memory:')
c = self.conn.cursor()
c.execute('CREATE TABLE video (word text, name_link text, explain text, link text)')
newrecs = (('python', 'Video1', 'test1', r'C:\video1.MP4'),
('python', 'Video2', 'test2', r'C:\video2.MP4'),
('notpython', 'Video3', 'test3', r'C:\video3.MP4'),
('python', 'Video4', 'test4', r'C:\video4.MP4'),
('python', 'Video5', 'test5', r'C:\video5.MP4'),
)
for tup in newrecs:
c.execute('INSERT INTO video VALUES (?, ?, ?, ?)', tup)
self.map_explain = {}
def get_videos(self, klmt):
# you want to get videos matching a parameter?
sqlstr = 'SELECT * FROM video WHERE video.word = (?)'
c = self.conn.cursor()
c.execute(sqlstr, (klmt.lower(),))
res = c.fetchall()
return res
class playframe(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
pnl = wx.Panel(self, -1)
self.explain = wx.StaticText(pnl, -1, 'Click in list to show explanation')
self.klmt = wx.TextCtrl(pnl, -1, '')
self.srch = wx.Button(pnl, -1, u'Search…')
self.vids = wx.ListBox(pnl, -1, style=wx.LB_MULTIPLE)
szmain = wx.BoxSizer(wx.VERTICAL)
szmain.Add(wx.StaticText(pnl, -1, 'Search for video category:'), 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.klmt, 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.srch, 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.vids, 1, wx.EXPAND|wx.ALL, 4)
szmain.Add(wx.StaticText(pnl, -1, 'Explanation for video'), 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.explain, 0, wx.EXPAND|wx.ALL, 4)
pnl.SetSizer(szmain)
szmain.Fit(self)
class controller(object):
def __init__(self, app):
self.model = play_model()
self.search_results = []
# print self.model.get_videos('python')
self.frm = playframe(None, -1, 'test_playframe')
self.frm.Show()
self.frm.srch.Bind(wx.EVT_BUTTON, self.on_srch)
self.frm.vids.Bind(wx.EVT_LISTBOX, self.onvid_dblclick)
def onvid_dblclick(self, evt):
sels = evt.GetEventObject().GetSelections()
for idx in sels:
self.frm.explain.SetLabel(self.search_results[idx][2])
print 'play video:', idx, self.search_results[idx]
def on_srch(self, evt):
klmt = self.frm.klmt.GetValue()
# print klmt
res = self.model.get_videos(klmt)
if res:
self.search_results = res
self.frm.vids.Clear()
self.frm.vids.AppendItems([row[1] for row in self.search_results])
else:
parent = self.frm
wx.MessageDialog(parent,
'Not found in word category: {0}'.format(klmt),
'Category not found').ShowModal()
if __name__ == '__main__':
app = wx.App(redirect=False)
controller(app)
app.MainLoop()
I was try create some code like this.
def playVideo(self, evt, temp):
self.player.Load(evt.GetClientObject())
self.Play(True)
self.pjs.Clear()
self.pjs.AppendText(evt.GetClientObject())
Its work for the video, the video can play. but for the information from column database explain, its not show. i want the information from column explain print in tb4. #nepix32
I want to use a graph that is generated by DOT (pyDot in python) as the basis for an interactive Tree-structured GUI in which each of the nodes in the Tree could be widgets.
The tree will basically be a binary Morse Code tree which start at the top node and navigate down the tree to their desired letter and select it. The node they want to select should be highlightable, and the contents (letters) of should be able to be changed based on user input.
Basically I want the nodes to be turned into full scale objects with tunable parameters that change as the interface is used. Can anyone point me in the right direction in order to do this?
I started with the demo code at: http://wiki.wxpython.org/AnotherTutorial#wx.TreeCtrl. I've added the build_tree, _build_tree_helper and build_conn_dict methods. The key methods of interest from the dot_parser library are edge.get_source() and edge.get_destination() which are used to make the "connection" dictionary.
The dot graph is stored in the dot_data variable. Importantly, the dot graph must not loop; that is, it must be a spanning tree otherwise the _build_tree_helper method will loop infinitely (and it doesn't make sense in a TreeControl).
I also had to patch dot_parser according to https://github.com/nlhepler/pydot-py3/issues/1#issuecomment-15999052 to get it to work.
import wx
from dot_parser import parse_dot_data
class MyFrame(wx.Frame):
def __init__(self, parent, id, title, **kwargs):
self.parsed_dot = kwargs.pop("parsed_dot", None)
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(450, 350))
hbox = wx.BoxSizer(wx.HORIZONTAL)
vbox = wx.BoxSizer(wx.VERTICAL)
panel1 = wx.Panel(self, -1)
panel2 = wx.Panel(self, -1)
self.tree = wx.TreeCtrl(panel1, 1, wx.DefaultPosition, (-1,-1), wx.TR_HAS_BUTTONS | wx.TR_LINES_AT_ROOT )
self.build_tree(self.tree)
self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1)
self.display = wx.StaticText(panel2, -1, "",(10,10), style=wx.ALIGN_CENTRE)
vbox.Add(self.tree, 1, wx.EXPAND)
hbox.Add(panel1, 1, wx.EXPAND)
hbox.Add(panel2, 1, wx.EXPAND)
panel1.SetSizer(vbox)
self.SetSizer(hbox)
self.Centre()
def build_conn_dict(self):
conn_dict = {}
if(self.parsed_dot):
for edge in self.parsed_dot.get_edges():
conn_dict.setdefault(edge.get_source(), []).append(edge.get_destination())
return conn_dict
def build_tree(self, tree):
if(self.parsed_dot):
conn_dict = self.build_conn_dict()
outs = set(conn_dict.keys())
ins = reduce(lambda x, y: x | set(y), conn_dict.values(), set([]))
roots = list(outs - ins)
roots = dict([(root, tree.AddRoot(root)) for root in roots])
self._build_tree_helper(tree, conn_dict, roots)
def _build_tree_helper(self, tree, conn_dict = {}, roots = {}):
new_roots = {}
for root in roots:
if(conn_dict.has_key(root)):
for sub_root in conn_dict[root]:
new_roots[sub_root] = tree.AppendItem(roots[root], sub_root)
if(new_roots):
self._build_tree_helper(tree, conn_dict, new_roots)
def OnSelChanged(self, event):
item = event.GetItem()
self.display.SetLabel(self.tree.GetItemText(item))
child_text = self.tree.GetItemText(item)
parent_text = ""
try:
parent = self.tree.GetItemParent(item)
parent_text = self.tree.GetItemText(parent)
except wx._core.PyAssertionError:
pass
print "child: %s, parent: %s" % (child_text, parent_text)
class MyApp(wx.App):
def OnInit(self):
dot_data = \
'''
graph ""
{
label="(% (EXP (% (X) (% (X) (X)))) (EXP (SIN (X))))"
n039 ;
n039 [label="%"] ;
n039 -> n040 ;
n040 [label="EXP"] ;
n040 -> n041 ;
n041 [label="%"] ;
n041 -> n042 ;
n042 [label="X"] ;
n041 -> n043 ;
n043 [label="%"] ;
n043 -> n044 ;
n044 [label="X"] ;
n043 -> n045 ;
n045 [label="X"] ;
n039 -> n046 ;
n046 [label="EXP"] ;
n046 -> n047 ;
n047 [label="SIN"] ;
n047 -> n048 ;
n048 [label="X"] ;
}
'''
parsed_dot = parse_dot_data(dot_data)
frame = MyFrame(None, -1, "treectrl.py", parsed_dot = parsed_dot)
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
Here is a snippet
self.list_ctrl = wx.ListCtrl(self, size=(-1,100),
style=wx.LC_ICON|wx.LC_ALIGN_LEFT
)
il = wx.ImageList(16,16,True)
png = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,wx.ART_OTHER, (16,16))
il.Add(png)
self.list_ctrl.AssignImageList(il,wx.IMAGE_LIST_NORMAL)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
self.list_ctrl.InsertImageStringItem(0,"1",0)
self.list_ctrl.InsertImageStringItem(1,"2",0)
My problem is that the icons appear to the top of the text which should not happen because I put wx.LC_ALIGN_LEFT in the style. I would like the icons to appear left of the text.
Another problem is that, I want one element per row. In my code it is almost like one element per column.
Can anyone help me with any of these problems?
Thanks.
Looking at the wxPython demo for the ListCtrl, it looks like they use SetImageList() instead of AssignImageList(). Not sure what the difference is though. I don't see where you're inserting any text though. You'd need to use SetStringItem to put text in the other columns from what I can see.
EDIT: Code from wxPython Demo package, ListCtrl demo:
self.il = wx.ImageList(16, 16)
self.idx1 = self.il.Add(images.Smiles.GetBitmap())
self.sm_up = self.il.Add(images.SmallUpArrow.GetBitmap())
self.sm_dn = self.il.Add(images.SmallDnArrow.GetBitmap())
And then we add data / images to the widget
def PopulateList(self):
if 0:
# for normal, simple columns, you can add them like this:
self.list.InsertColumn(0, "Artist")
self.list.InsertColumn(1, "Title", wx.LIST_FORMAT_RIGHT)
self.list.InsertColumn(2, "Genre")
else:
# but since we want images on the column header we have to do it the hard way:
info = wx.ListItem()
info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
info.m_image = -1
info.m_format = 0
info.m_text = "Artist"
self.list.InsertColumnInfo(0, info)
info.m_format = wx.LIST_FORMAT_RIGHT
info.m_text = "Title"
self.list.InsertColumnInfo(1, info)
info.m_format = 0
info.m_text = "Genre"
self.list.InsertColumnInfo(2, info)
items = musicdata.items()
for key, data in items:
index = self.list.InsertImageStringItem(sys.maxint, data[0], self.idx1)
self.list.SetStringItem(index, 1, data[1])
self.list.SetStringItem(index, 2, data[2])
self.list.SetItemData(index, key)