When I try to run the code below, I get this error:
Can't create window of class wxWindowNR (error 1406: cannot create a top-level child window.)
What is the problem here?
Code:
import os
import sys
import wx
from datetime import datetime, timedelta
szflags = wx.EXPAND | wx.ALL
min_height = 50
height_ratio = 4
pborder = 10
lborder = 5
class TangoArtProvider(wx.ArtProvider):
def __init__(self):
super(TangoArtProvider, self).__init__()
self.path = os.path.join(os.getcwd(), "icons")
self.bmps = [bmp.replace('.png', '')
for bmp in os.listdir(self.path)
if bmp.endswith('.png')]
print self.bmps
def CreateBitmap(self, id, client=wx.ART_OTHER, size=wx.DefaultSize):
if wx.Platform == '__WXGTK__':
return wx.NullBitmap
bmp = wx.NullBitmap
if id in self.bmps:
path = os.path.join(self.path, id+'.png')
bmp = wx.Bitmap(path)
else:
pass
return bmp
class ChartPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.SetBackgroundColour('GREEN')
self.st = wx.StaticText(self, label='CHART PANEL')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.st, 0 , szflags , lborder)
self.SetSizer(sizer)
class NotebookPage(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.chartPanel = ChartPanel(self, name='Notebook_Page_ChartPanel')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.chartPanel, height_ratio, szflags, pborder)
self.SetSizer(sizer)
class Notebook(wx.Notebook):
def __init__(self, *args, **kwargs):
wx.Notebook.__init__(self, *args, **kwargs)
#self.SetBackgroundColour('PINK')
self.nbPages = {}
fleets = ["333"]
page_ix = 0
for fleet in fleets:
self.nbPages[fleets.index(fleet)] = NotebookPage(self, name='NotebookPage0')
for fleet in fleets:
self.AddPage(self.nbPages[fleets.index(fleet)], fleet, True)
class ChartPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
#self.st = wx.StaticText(self, label='Chart Panel')
self.noteBook = Notebook(self, name='Notebook')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.noteBook, 1, szflags)
self.SetSizer(sizer)
class SettingsPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.st = wx.StaticText(self, label='Settings Panel')
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.st, 0, szflags)
self.SetSizer(self.sizer)
class OutputPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.st = wx.StaticText(self, label='Output Panel')
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.st, 0, szflags)
self.SetSizer(self.sizer)
class ProportionalSplitter(wx.SplitterWindow):
def __init__(self,parent, id = -1, proportion=0.66, size = wx.DefaultSize, **kwargs):
wx.SplitterWindow.__init__(self,parent,id,wx.Point(0, 0),size, **kwargs)
self.SetMinimumPaneSize(50) #the minimum size of a pane.
self.proportion = proportion
if not 0 < self.proportion < 1:
raise ValueError, "proportion value for ProportionalSplitter must be between 0 and 1."
self.ResetSash()
self.Bind(wx.EVT_SIZE, self.OnReSize)
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged, id=id)
##hack to set sizes on first paint event
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.firstpaint = True
def SplitHorizontally(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitHorizontally(self, win1, win2,
int(round(self.GetParent().GetSize().GetHeight() * self.proportion)))
def SplitVertically(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitVertically(self, win1, win2,
int(round(self.GetParent().GetSize().GetWidth() * self.proportion)))
def GetExpectedSashPosition(self):
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
return int(round(tot * self.proportion))
def ResetSash(self):
self.SetSashPosition(self.GetExpectedSashPosition())
def OnReSize(self, event):
"Window has been resized, so we need to adjust the sash based on self.proportion."
self.ResetSash()
event.Skip()
def OnSashChanged(self, event):
"We'll change self.proportion now based on where user dragged the sash."
pos = float(self.GetSashPosition())
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
self.proportion = pos / tot
event.Skip()
def OnPaint(self,event):
if self.firstpaint:
if self.GetSashPosition() != self.GetExpectedSashPosition():
self.ResetSash()
self.firstpaint = False
event.Skip()
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.SetBackgroundColour('YELLOW')
self.CreateStatusBar(2)
self.SetStatusWidths([-2, -1])
#=======================================================================
# toolbar = self.CreateToolBar()
# toolbar.SetToolBitmapSize((20,20))
# icon_size = (32,32)
#
# wx.ArtProvider.PushProvider(TangoArtProvider())
#
# run = wx.ArtProvider.GetBitmap("forward", wx.ART_TOOLBAR, icon_size)
# settings = wx.ArtProvider.GetBitmap("tools", wx.ART_TOOLBAR, icon_size)
# quit = wx.ArtProvider.GetBitmap("quit", wx.ART_TOOLBAR, icon_size)
# toolbar.AddSimpleTool(1, run, "Run", "Run the report")
# toolbar.AddSimpleTool(2, settings, "Settings", "Change program settings")
# toolbar.AddSimpleTool(3, quit, "Quit", "Exit Program")
#
# toolbar.Realize()
#=======================================================================
self.split1 = ProportionalSplitter(self,-1, 0.66)
self.split2 = ProportionalSplitter(self.split1,-1, 0.50)
print "line 200"
self.rightpanel = ChartPanel(self.split1)
self.rightpanel.SetBackgroundColour('cyan')
print "line 203"
self.topleftpanel = SettingsPanel(self.split2)
self.topleftpanel.SetBackgroundColour('pink')
print "line 206"
self.bottomleftpanel = OutputPanel(self.split2)
self.bottomleftpanel.SetBackgroundColour('sky Blue')
## add your controls to the splitters:
self.split1.SplitVertically(self.split2, self.rightpanel)
self.split2.SplitHorizontally(self.topleftpanel, self.bottomleftpanel)
class App(wx.App):
def OnInit(self):
self.mainFrame = MainFrame(None,
size=(1000, 80*10),
title='Report', name='MainFrame')
wx.GetApp().SetTopWindow( self.mainFrame )
self.mainFrame.Show()
return True
def main(*args, **kwargs):
"""A nice place to process things before MainLoop"""
global app
import wx.lib.inspection
app = App()
#wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
if __name__ == "__main__":
app = None
main()
There is a infinite loop in your code:
ChartPanel --> Notebook --> NotebookPage --> ChartPanel
Related
I have a code. When it runs, you click on add contact button and add some contact. When you press on each contact, it just shows you the last contact information you have added. But when I press on each contact, I want it to instead show me the information for the contact I have clicked. Who can help me to solve this problem?
import wx
from wx._core import RB_GROUP, TE_READONLY
import sys
import os
from _ssl import nid2obj
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,500))
self.panel = wx.Panel(self, -1)
self.contactslist =[]
self.path = os.getcwd()+'\\contacts-db'
self.nc = wx.Button(self.panel, label='new contact')
self.nc.Bind(wx.EVT_BUTTON, self.newcontact)
self.displaycontacts()
self.Show(True)
def displaycontacts(self):
for roots, dirs, files in os.walk(self.path):
for filename in files:
#self.contactslist.append(filename)
self.cntct = wx.Button(self.panel, label=filename)
self.cntct.Bind(wx.EVT_BUTTON, self.clickcontacts)
def clickcontacts(self, e):
self.oc =self.path+'\\'+self.cntct.GetLabel()
self.oc2 = open(self.oc, 'r')
prt = wx.TextCtrl(self.panel, value=self.oc2.read())
def newcontact(self, e):
self.val = ''
self.f1 = wx.TextCtrl(self.panel, value='enter your first name', pos=(0,0))
self.f1.SetFocus()
self.f2 = wx.TextCtrl(self.panel, value='enter your last name', pos=(0,50))
self.rbm = wx.RadioButton(self.panel, label='male', style=RB_GROUP, pos=(0,100))
self.rbm.Bind(wx.EVT_RADIOBUTTON, self.male)
self.rbf = wx.RadioButton(self.panel, label='female')
self.rbf.Bind(wx.EVT_RADIOBUTTON, self.female)
self.btn = wx.Button(self.panel, label='show', pos=(0,200))
self.btn.Bind(wx.EVT_BUTTON, self.showform)
self.s = wx.TextCtrl(self.panel, value=self.val, style=wx.TE_MULTILINE, size=(800,800))
self.clrbtn = wx.Button(self.panel, label='clear', pos=(800,800))
self.clrbtn.Bind(wx.EVT_BUTTON, self.onclear)
self.af = wx.Button(self.panel, label='add additional field')
self.af.Bind(wx.EVT_BUTTON, self.additionalfield)
self.sv = wx.Button(self.panel, label='save')
self.sv.Bind(wx.EVT_BUTTON, self.save)
def showform(self, e):
self.s.SetValue('first name: '+self.f1.GetValue()+'\nlast name: '+self.f2.GetValue()+self.val)
def male(self, e):
self.val=''
self.val = '\ngender: male'
def female(self, e):
self.val=''
self.val='\ngender: female'
def onclear(self, e):
self.s.Clear()
self.f1.Clear()
self.f2.Clear()
def additionalfield(self, e):
self.x = wx.TextCtrl(self.panel, value='\nenter field name: ')
self.x.SetFocus()
self.y = wx.TextCtrl(self.panel, value='enter information ')
self.add = wx.Button(self.panel, label='add')
self.add.Bind(wx.EVT_BUTTON, self.additionalfieldappend)
def additionalfieldappend(self, e):
self.s.AppendText('\n'+self.x.GetValue()+': '+self.y.GetValue())
self.x.Destroy()
self.y.Destroy()
def save(self, e):
if not os.path.exists(self.path):
os.makedirs(self.path)
cnt = open(self.path+'\\'+self.f1.GetValue()+self.f2.GetValue()+'.txt', 'x')
cnt.write(self.s.GetValue())
nid2obj
app = wx.App()
frame = MyFrame(None, 'contact form')
app.MainLoop()
Everyone will develop their own style when using wxPython. As Rolf has suggested, you do not just add widgets to a frame, but you need to add a panel. I have added a basic contact add screen below. The 'New' button creates an instance of a wx.Dialog, which captures the details of a contact. These are then available to the MainFrame where you can save them if you want.
Note that I have freely created new classes in the application to handle the different areas of concern. GUI apps can become very complex, and in my opinion, there is no substitute for factoring each bit of functionality into its own method or class.
Not everything needs to be a member of the frame class.
Note also the I have used sizers and not absolute positions.
This code does not do everything that you want to do, but it might help get you started.
import wx
BORDER = 5
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super().__init__(None, *args, **kwargs)
self.size = (400, 1000)
self.Title = 'Contacts'
self.Bind(wx.EVT_CLOSE, self.on_quit_click)
self.panel = MainPanel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.panel)
self.SetSizer(sizer)
self.Center()
self.Show()
def new_contact(self, event):
del event
contact_add_screen = NewContact(self)
try:
contact_add_screen.ShowModal()
if contact_add_screen.state == wx.ID_OK:
print(contact_add_screen.first_name)
print(contact_add_screen.last_name)
# This is where you can save your contacts
finally:
contact_add_screen.Destroy()
def on_quit_click(self, event):
del event
wx.CallAfter(self.Destroy)
class MainPanel(wx.Panel):
"""Create a panel class to contain screen widgets."""
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.parent = parent
cmd_new_contact = wx.Button(self, id= wx.ID_NEW)
cmd_new_contact.Bind(wx.EVT_BUTTON, parent.new_contact)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(cmd_new_contact, flag=wx.ALL, border = BORDER)
self.SetSizer(sizer)
class NewContact(wx.Dialog):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.state = None
self.first_name = None
self.last_name = None
self.parent = parent
self.SetTitle('Add Contact')
self.panel = ContactAddPanel(self)
sizer = wx.BoxSizer()
sizer.Add(self.panel)
self.SetSizerAndFit(sizer)
def on_cmd_save_click(self, event):
del event
self.first_name = self.panel.txt_first_name.GetValue()
self.last_name = self.panel.txt_last_name.GetValue()
self.state = wx.ID_OK
self.Destroy()
def on_quit_click(self, event):
del event # unused
self.Destroy()
class ContactAddPanel(wx.Panel):
TEXT_SIZE = (450, -1)
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.parent = parent
contact_sizer = self._contact_sizer()
button_panel = ButtonPanel(self, wx.ID_SAVE,
self.parent.on_cmd_save_click,
self.parent.on_quit_click)
self.txt_first_name.SetFocus()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(contact_sizer)
sizer.Add(button_panel, flag=wx.EXPAND|wx.TOP, border=BORDER)
main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(sizer, flag=wx.ALL, border=BORDER)
self.SetSizer(main_sizer)
def _contact_sizer(self):
lbl_first_name= wx.StaticText(self, label='First name: ')
self.txt_first_name= wx.TextCtrl(self, size=self.TEXT_SIZE)
lbl_last_name = wx.StaticText(self, label='Last name:')
self.txt_last_name = wx.TextCtrl(self, size=self.TEXT_SIZE)
sizer = wx.GridBagSizer(BORDER, BORDER)
sizer.Add(lbl_first_name, pos=(0, 0), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.txt_first_name, pos=(0, 1))
sizer.Add(lbl_last_name, pos=(1, 0), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.txt_last_name, pos=(1, 1))
return sizer
class ButtonPanel(wx.Panel):
def __init__(self, parent, ok_id, ok_bind, cancel_bind, *args, **kwargs):
super(ButtonPanel, self).__init__(parent, *args, **kwargs)
self.cmd_action = wx.Button(self, ok_id)
cmd_cancel = wx.Button(self, wx.ID_CANCEL)
self.cmd_action.Bind(wx.EVT_BUTTON, ok_bind)
cmd_cancel.Bind(wx.EVT_BUTTON, cancel_bind)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.cmd_action)
sizer.Add((0, 0), proportion=1)
sizer.Add(cmd_cancel)
self.SetSizer(sizer)
if __name__ == '__main__':
wx_app = wx.App()
MainFrame()
wx_app.MainLoop()
After my last post i found out that i can combine ComboCtrl with an Checklistbox Popup. So far so good. Now i try to figure out why EVT_CHECKLISTBOX doesn't work properly. Did i bound it the wrong way?
self.lc.Bind(wx.EVT_CHECKLISTBOX, self.OnSelect)
Also:
how do i make the popup fit the content? At the moment it is pretty huge
how do i change the Combobox that it doesnt fill the entire window anymore?
Here is my code so far:
import wx
import wx.stc
from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):
def __init__(self, parent):
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT |
wx.SUNKEN_BORDER)
CheckListCtrlMixin.__init__(self)
ListCtrlAutoWidthMixin.__init__(self)
class ListViewComboPopup(wx.ComboPopup):
def __init__(self):
wx.ComboPopup.__init__(self)
self.lc = None
def AddItem(self, txt):
self.lc.InsertItem(0, txt)
def OnSelect(self, event):
print("Working fine!")
def Init(self):
self.value = -1
self.curitem = -1
def Create(self, parent):
self.lc = CheckListCtrl(parent)
self.lc.InsertColumn(0, '', width=90)
self.lc.Bind(wx.EVT_CHECKLISTBOX, self.OnSelect)
return True
def GetControl(self):
return self.lc
def OnPopup(self):
wx.ComboPopup.OnPopup(self)
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Popup Menu Tutorial")
comboCtrl = wx.ComboCtrl(self, wx.ID_ANY, "")
popupCtrl = ListViewComboPopup()
comboCtrl.SetPopupControl(popupCtrl)
popupCtrl.AddItem("Test 1")
popupCtrl.AddItem("Test 2")
popupCtrl.AddItem("Test 3")
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()
OnCheckItem is called by CheckListCtrlMixin.
Add a panel to the frame and set the ComboCtrl's parent as the panel
Change the ComboPopup's method GetAdjustedSize return value to alter the size
import wx
import wx.stc
from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):
def __init__(self, parent):
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT |
wx.SUNKEN_BORDER)
CheckListCtrlMixin.__init__(self)
ListCtrlAutoWidthMixin.__init__(self)
self.SetSize(-1, -1, -1, 50)
def OnCheckItem(self, index, flag):
item = self.GetItem(index)
if flag:
what = "checked"
else:
what = "unchecked"
print(f'{item.GetText()} - {what}')
class ListViewComboPopup(wx.ComboPopup):
def __init__(self):
wx.ComboPopup.__init__(self)
self.lc = None
def AddItem(self, txt):
self.lc.InsertItem(0, txt)
def Init(self):
self.value = -1
self.curitem = -1
def Create(self, parent):
self.lc = CheckListCtrl(parent)
self.lc.InsertColumn(0, '', width=90)
return True
def GetControl(self):
return self.lc
def OnPopup(self):
wx.ComboPopup.OnPopup(self)
def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
return wx.ComboPopup.GetAdjustedSize(
self, minWidth, 110, maxHeight)
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Popup Menu Tutorial")
panel = wx.Panel(self)
comboCtrl = wx.ComboCtrl(panel, wx.ID_ANY, "Select filter")
popupCtrl = ListViewComboPopup()
comboCtrl.SetPopupControl(popupCtrl)
popupCtrl.AddItem("Test 1")
popupCtrl.AddItem("Test 2")
popupCtrl.AddItem("Test 3")
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()
i use wxpython for python3
i try to creat my simple application and i 'm try to create the menu and with 2 panels
i need to add a file dialog when the user click in the button OPEN in the menu to chose the file , i don't know how can add it in my code
this is my code :
import wx
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(None, *args, **kwargs)
self.Title = 'premier app (Menu+2windows)'
self.SetMenuBar(MenuBar(self))
self.ToolBar = MainToolbar(self)
self.status_bar = StatusBar(self).status_bar
self.Bind(wx.EVT_CLOSE, self.on_quit_click)
panel = MainPanel(self)
sizer = wx.BoxSizer()
sizer.Add(panel)
self.SetSizerAndFit(sizer)
self.Centre()
self.Show()
def on_quit_click(self, event):
del event
wx.CallAfter(self.Destroy)
class MainPanel(wx.Panel):
def __init__(self, parent):
wx.Frame.__init__(self, parent,size = (500,500))
self.splitter = wx.SplitterWindow(self, -1, size = (500,500))
# 1er panel
pan1 = wx.Window(self.splitter, style=wx.BORDER_SUNKEN)
pan1.SetBackgroundColour("yellow")
wx.StaticText(pan1, -1)
#2em panel
pan2 = wx.Window(self.splitter, style=wx.BORDER_SUNKEN)
pan2.SetBackgroundColour("blue")
wx.StaticText(pan2, -1)
self.splitter.SplitVertically(pan1, pan2, 100)
class MenuBar(wx.MenuBar):
"""creation de menu."""
def __init__(self, parent, *args, **kwargs):
super(MenuBar, self).__init__(*args, **kwargs)
# menu
File_menu = wx.Menu()
Edit_menu = wx.Menu()
Help_menu = wx.Menu()
self.Append(File_menu, '&File')
self.Append(Edit_menu, '&Edit')
self.Append( Help_menu, '&Help')
quit_menu_item = wx.MenuItem(File_menu, wx.ID_EXIT)
parent.Bind(wx.EVT_MENU, parent.on_quit_click, id=wx.ID_EXIT)
open_menu_item = wx.MenuItem(File_menu, wx.ID_OPEN)
new_menu_item = wx.MenuItem(File_menu,wx.ID_NEW)
File_menu.Append(open_menu_item)
File_menu.Append(new_menu_item)
File_menu.Append(quit_menu_item)
class MainToolbar(wx.ToolBar):
"""creation toolbar."""
def __init__(self, parent, *args, **kwargs):
super(MainToolbar, self).__init__(parent, *args, **kwargs)
class StatusBar(object):
def __init__(self, parent):
self.status_bar = parent.CreateStatusBar()
if __name__ == '__main__':
"""Run the application."""
screen_app = wx.App()
main_frame = MainFrame()
screen_app.MainLoop()
need some help
thank u
With this code:
quit_menu_item = wx.MenuItem(File_menu, wx.ID_EXIT)
parent.Bind(wx.EVT_MENU, parent.on_quit_click, id=wx.ID_EXIT)
you are binding the Quit menu item to a Quit function.
Do the same for the Open function i.e. Bind it to something like parent.on_open_file
In that function, do something like this.
def self.on_open_file(self, event):
dlg = wx.FileDialog(self, message="Choose file", defaultDir = "/home", defaultFile = "",\
wildcard = "", style=wx.FD_OPEN)
if dlg.ShowModal() == wx.ID_OK:
print(dlg.GetDirectory(), dlg.GetFilename())
dlg.Destroy()
There is an existing question on this subject: Find text dialog with wxpython, but it's in TextCtrl. So I changed the TextCtrl to StyledTextCtrl, and tested it. But I got this error:
in wxStyledTextCtrl::SetStyle(): not implemented
How do I make the SetStyle a selection instead so that you can click away? Here is my code:
import wx
import wx.stc as stc
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.tc = stc.StyledTextCtrl(self, style=wx.TE_MULTILINE | wx.TE_WORDWRAP)
self.bt_find = wx.Button(self, -1, "find")
self.Bind(wx.EVT_BUTTON, self.on_button, self.bt_find)
self.Bind(wx.EVT_FIND, self.on_find)
self.pos = 0
self.size = 0
#
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.tc, 1, wx.EXPAND, 0)
sizer.Add(self.bt_find, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
self.SetSizer(sizer)
sizer.Fit(self)
self.Layout()
def on_button(self, event):
self.txt = self.tc.GetValue()
self.data = wx.FindReplaceData() # initializes and holds search parameters
self.dlg = wx.FindReplaceDialog(self.tc, self.data, 'Find')
self.dlg.Show()
def on_find(self, event):
fstring = self.data.GetFindString() # also from event.GetFindString()
self.pos = self.txt.find(fstring, self.pos)
self.size = len(fstring)
self.tc.SetStyle(self.pos, self.pos+self.size, wx.TextAttr("red", "black"))
if __name__ == "__main__":
app = wx.PySimpleApp(0)
frame_1 = MyFrame(None, wx.ID_ANY, "")
frame_1.Show()
app.MainLoop()
The StyledTextCtrl looks like a highly complex beast and I can only assume that you will have to read the scintilla documents exhustively http://www.scintilla.org/
The link in my comment was pointing at using SetStyling function not AddSelection.
This is what I managed using that:
import wx
import wx.stc as stc
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.tc = stc.StyledTextCtrl(self, style=wx.TE_MULTILINE | wx.TE_WORDWRAP)
self.bt_find = wx.Button(self, -1, "find")
self.Bind(wx.EVT_BUTTON, self.on_button, self.bt_find)
self.Bind(wx.EVT_FIND, self.on_find)
self.pos = 0
self.size = 0
#
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.tc, 1, wx.EXPAND, 0)
sizer.Add(self.bt_find, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
self.SetSizer(sizer)
sizer.Fit(self)
self.Layout()
def on_button(self, event):
self.txt = self.tc.GetValue()
self.data = wx.FindReplaceData() # initializes and holds search parameters
dlg = wx.FindReplaceDialog(self.tc, self.data, 'Find')
dlg.Show()
def on_find(self, event):
self.tc.StartStyling(pos=0, mask=0xFF)
self.tc.SetStyling(length=len(self.txt), style=0)
fstring = event.GetFindString()
self.size = len(fstring)
while True:
self.pos = self.txt.find(fstring, self.pos)
if self.pos < 0:
break
self.tc.StyleSetSpec(1, "fore:#FF0000,back:#000000")
self.tc.StartStyling(pos=self.pos, mask=0xFF)
self.tc.SetStyling(length=self.size, style=1)
self.pos += 1
self.pos = 0
if __name__ == "__main__":
app = wx.App()
frame_1 = MyFrame(None, wx.ID_ANY, "")
frame_1.Show()
app.MainLoop()
I have seen questions where you can share variables between classes -- which I can normally do fairly easily. However, I am trying to share a variables between a wx.Frame and a custom list control. Essentially, I have a class with an Editable ListCtrl which uses a dictionary to sort values in a list. I have normally just made the dictionary a global variable, but I am attempting to completely remove global variables from my code. How do I reference the dictionary in the ListCtrl class from the wx.Frame class? below is a snippet of the code.
Thanks!
class TMainForm(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.Splitter = wx.SplitterWindow(self, -1)
self.Panel1 = wx.Panel(self.Splitter, -1)
self.Panel3 = wx.Panel(self.Splitter, -1)
self.Splitter.SplitVertically(self.Panel1,self.Panel3,450)
self.Notebook2 = wx.Notebook(self.Panel1, -1)
# This is the Panel my list is in
self.List1 = scrolled.ScrolledPanel(self.Notebook2, -1)
self.List1.SetupScrolling()
# This custom function creates the dictionary for list 1 (labeled self.List1Data)
self.CreateDictionary()
# This sets up the listctrl to reference the class EditableListCtrl
self.List1_list = EditableListCtrl(self.List1,-1,style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
self.List1_list.InsertColumn(1,'Col 1')
self.List1_list.InsertColumn(2,'Col 2',format=wx.LIST_FORMAT_CENTRE)
items = self.List1Data.items()
index = 0
for key, data in items:
self.List1_list.InsertStringItem(index, data[0])
self.List1_list.SetStringItem(index, 1, data[1])
self.List1_list.SetItemData(index, key)
index += 1
self.List1_list.itemDataMap = self.List1Data
#...
# In this class i have "List1Data" which is a global variable. I want to grab self.List1Data
# from the TMainForm class and use it in here (instead of the List1Data global variable).
class EditableListCtrl(wx.ListCtrl, listmix.TextEditMixin, listmix.ColumnSorterMixin):
def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=0):
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
listmix.ColumnSorterMixin.__init__(self, len(List1Data))
self.itemDataMap = List1Data
def GetListCtrl(self):
return self
Trying to follow suggested answer below:
class EditableListCtrl(wx.ListCtrl, listmix.TextEditMixin, listmix.ColumnSorterMixin):
def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=0):
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
listmix.ColumnSorterMixin.__init__(self, len(self.List1Data))
self.itemDataMap = self.List1Data
def get_dict(self):
return self.List1Data
def GetListCtrl(self):
return self
* EDIT **
Full runnable code for my issue. See comment -- I want to be able to reference self.List1Data in TMainForm in the EditableListCtrl class. Thanks!
import os
import wx
import wx.lib.mixins.listctrl as listmix
import wx.lib.scrolledpanel as scrolled
class TMainForm(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.Splitter = wx.SplitterWindow(self, -1)
self.Panel1 = wx.Panel(self.Splitter, -1)
self.Panel3 = wx.Panel(self.Splitter, -1)
self.Splitter.SplitVertically(self.Panel1,self.Panel3,450)
self.Notebook2 = wx.Notebook(self.Panel1, -1)
self.List1 = scrolled.ScrolledPanel(self.Notebook2, -1)
self.List1.SetupScrolling()
self.UpdateData()
self.List1_list = EditableListCtrl(self.List1,-1,style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
self.List1_list.InsertColumn(1,'Col 1')
self.List1_list.InsertColumn(2,'Col 2',format=wx.LIST_FORMAT_CENTRE)
self.Update_List()
self.List1_list.SetColumnWidth(0,-2)
self.List1_list.SetColumnWidth(1,-2)
self.Notebook3 = wx.Notebook(self.Panel3, -1)
self.OptPane = scrolled.ScrolledPanel(self.Notebook3, -1)
self.OptPane.SetupScrolling()
self.__set_properties()
self.__do_layout()
def __set_properties(self): # Set GUI Title and Size
self.screen_x = 95 * wx.GetDisplaySize()[0]/100
self.screen_y = 90 * wx.GetDisplaySize()[1]/100
if wx.GetDisplaySize()[0] < 1920:
self.Maximize(True)
else:
self.SetSize((self.screen_x, self.screen_y))
self.SetMinSize((1280,768))
self.SetFocus()
def __do_layout(self , call_fit = True, set_sizer = True):
sizer_22 = wx.BoxSizer(wx.VERTICAL)
sizer_22.Add(self.List1_list,1,wx.EXPAND)
self.List1.SetSizer(sizer_22)
self.Notebook2.AddPage(self.List1,"List1")
sizer_P2 = wx.BoxSizer(wx.VERTICAL)
sizer_P2.Add(self.Notebook2, 1, wx.EXPAND, 0)
self.Panel1.SetSizer(sizer_P2)
sizer_P3 = wx.BoxSizer(wx.VERTICAL)
sizer_P3.Add(self.Notebook3, 1, wx.EXPAND, 0)
self.Panel3.SetSizer(sizer_P3)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.Splitter,1,wx.EXPAND)
self.SetSizer(sizer)
self.Layout()
self.Centre()
def Update_List(self):
#items = self.List1Data.items()
items = LD1.items()
index = 0
for key, data in items:
self.List1_list.InsertStringItem(index, str(data[0]))
self.List1_list.SetStringItem(index, 1, str(data[1]))
self.List1_list.SetItemData(index, key)
index += 1
#self.List1_list.itemDataMap = self.List1Data
self.List1_list.itemDataMap = LD1
def UpdateData(self):
self.List1Data = {}
self.List1Data[0] = (1,2,3)
self.List1Data[1] = (4,5,6)
global LD1
LD1 = self.List1Data
class EditableListCtrl(wx.ListCtrl, listmix.TextEditMixin, listmix.ColumnSorterMixin):
# I want to be able to get self.List1Data from TMainForm here instead of LD1
#print TMainForm.List1Data
def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=0):
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
listmix.ColumnSorterMixin.__init__(self, len(LD1))
self.itemDataMap = LD1
def get_dict(self):
return self.List1Data
def GetListCtrl(self):
return self
app = wx.App(0)
# Code to Execute File
class TApplication(wx.App):
def OnInit(self):
wx.InitAllImageHandlers()
MainForm = TMainForm(None, -1,"")
self.SetTopWindow(MainForm)
MainForm.Show()
return 1
if __name__ == "__main__":
Application = TApplication(0)
Application.MainLoop()
Error when trying to access List1Data in TMainForm:
Traceback (most recent call last):
File "C:/Users/Me/Documents/test.py", line 103, in <module>
class EditableListCtrl(wx.ListCtrl, listmix.TextEditMixin, listmix.ColumnSorterMixin):
File "C:/Users/Me/Documents/test.py", line 106, in EditableListCtrl
print TMainForm.List1Data
AttributeError: type object 'TMainForm' has no attribute 'List1Data'
class EditableListCtrl(wx.ListCtrl, listmix.TextEditMixin, listmix.ColumnSorterMixin):
List1Data={...dictionary item....}
def __init__(self):
### your other methods now
You can access your List1Data from any place in other classes just using:
EditableListCtrl.List1Data
since now, List1Data is the part of your class definition.