Make a dynamic scrolledPanel more efficient in wxPython - python

In my app, I want to handle allocation / deallocation of sizers inside a scrolledPanel. In my first try, I was hiding / showing the contents of the sizers, but this was causing a lot of problems. The hidden sizers would "stay there", effectively ocuppying space, it would not update propely, etc. The solution that worked better was to destroy and create them over and over again.
But this that its problems too. The scrolledPanel blinks while I'm updating it and seems to be resource heavy. In my real app, I put the references of the buttons and checkboxes in lists and it becames more dificult to handle them.
So, if anyone has a better solution, I'm all ears!
I'm up to a hiding / showing solution! Something to reset the size of the scrolledPanel to accomodate only the sizer it currently has it would be nice too.
Thanks!
import wx
import wx.lib.scrolledpanel as scrolled
class WaterDataBase(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.setupSizers()
self.setupData()
def setupSizers(self):
masterSizer = wx.BoxSizer(wx.HORIZONTAL)
itemsSizer = wx.BoxSizer(wx.VERTICAL)
itemsSizer.Add(self.addSearchControl(), flag=wx.ALL, border=7)
self.scrolledPanelSizer = wx.BoxSizer(wx.VERTICAL)
self.scrolled_panel = scrolled.ScrolledPanel(self, wx.ID_ANY, size=(200, 200))
self.scrolled_panel.SetSizer(self.scrolledPanelSizer)
itemsSizer.Add(self.scrolled_panel, flag=wx.ALL, border=7)
masterSizer.Add(itemsSizer)
self.scrolled_panel.SetupScrolling()
self.SetSizer(masterSizer)
def addSearchControl(self):
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.searchField = wx.TextCtrl(self, -1)
self.searchField.Bind(wx.EVT_TEXT, self.OnSearched)
sizer.Add(self.searchField)
return sizer
def setupData(self):
self.words = "I'm trying to make this work, please. Let's keep it on! The day is beautiful today. Together we are stronger!".split()
for word in self.words:
self.addSizerToPanel(word)
def createSizer(self, word):
# Creates a sizer with a CheckBox and a StaticText to display.
sizer = wx.BoxSizer(wx.HORIZONTAL)
checkBox = wx.CheckBox(self.scrolled_panel, -1)
text = wx.StaticText(self.scrolled_panel, -1, word)
sizer.Add(checkBox, flag=wx.ALL, border=5)
sizer.Add(text, flag=wx.LEFT, border=5)
return sizer
def addSizerToPanel(self, word):
sizer = self.createSizer(word)
self.scrolledPanelSizer.Add(sizer, flag=wx.ALL, border=5)
def OnSearched(self, event):
query = self.searchField.GetValue().lower()
result = []
# If query's empty, print all words
if not query or query.isspace():
for word in self.words:
result.append(word)
else:
for word in self.words:
if word.lower().find(query) != -1:
result.append(word)
# Destroy all panel sizers and put exactly the ones we want.
self.scrolled_panel.DestroyChildren()
for word in result:
self.addSizerToPanel(word)
self.scrolled_panel.Layout()
self.scrolled_panel.Scroll(0, 0) # Using this to cause the scrollPanel get back to the top.
app = wx.App()
frame = WaterDataBase(None).Show()
app.MainLoop()
So, it seems I've made it, finally. I still wants the advice about reseting the size of the scrolledPanel. xD
import wx
import wx.lib.scrolledpanel as scrolled
class Frame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.sizerRefs = []
self.words = []
self.setupSizers()
self.setupData()
def setupSizers(self):
masterSizer = wx.BoxSizer(wx.HORIZONTAL)
itemsSizer = wx.BoxSizer(wx.VERTICAL)
itemsSizer.Add(self.addSearchControl(), flag=wx.ALL, border=7)
self.scrolledPanelSizer = wx.BoxSizer(wx.VERTICAL)
self.scrolled_panel = scrolled.ScrolledPanel(self, wx.ID_ANY, size=(200, 200))
self.scrolled_panel.SetSizer(self.scrolledPanelSizer)
itemsSizer.Add(self.scrolled_panel, flag=wx.ALL, border=7)
masterSizer.Add(itemsSizer)
self.scrolled_panel.SetupScrolling()
self.SetSizer(masterSizer)
def addSearchControl(self):
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.searchField = wx.TextCtrl(self, -1)
self.searchField.Bind(wx.EVT_TEXT, self.OnSearched)
sizer.Add(self.searchField)
return sizer
def setupData(self):
self.words = "I'm trying to make this work, please. Let's keep it on! The day is beautiful today. Together we are stronger!".split()
for i in range(0, len(self.words)):
self.addSizerToPanel(i)
def createSizer(self, index):
sizer = wx.BoxSizer(wx.HORIZONTAL)
checkBox = wx.CheckBox(self.scrolled_panel, -1)
text = wx.StaticText(self.scrolled_panel, -1, self.words[index])
sizer.Add(checkBox, flag=wx.ALL, border=5)
sizer.Add(text, flag=wx.LEFT, border=5)
self.sizerRefs.append(sizer)
return sizer
def addSizerToPanel(self, index):
sizer = self.createSizer(index)
self.scrolledPanelSizer.Add(sizer, flag=wx.ALL, border=5)
def hideAllSizers(self):
for sizer in self.sizerRefs:
sizer.ShowItems(False)
def unhideSizer(self, index):
self.sizerRefs[index].ShowItems(True)
def OnSearched(self, event):
query = self.searchField.GetValue().lower()
result = [] # Storing the indexes of the words found
# If query's empty, print all words
if not query or query.isspace():
for i in range(0, len(self.words)):
result.append(i)
else:
for i in range(0, len(self.words)):
if self.words[i].lower().find(query) != -1:
result.append(i)
# Hides all panel sizers and unhide exactly the ones we want.
self.hideAllSizers()
for i in range(0, len(result)):
self.unhideSizer(result[i])
self.scrolled_panel.Layout()
self.scrolled_panel.Scroll(0, 0) # Using this to cause the scrollPanel get back to the top.
app = wx.App()
frame = Frame(None).Show()
app.MainLoop()

Related

Button generating (wx.Python, Python)

I am developing a wxpython, i am looking for button generating, for example.
In text box enter the value how many button have to generate.
While submitting that, i have to show in panel as many as button
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Background Reset Tutorial",size=wx.Size(500,500))
# Add a panel so it looks the correct on all platforms
self.panel = wx.Panel(self, wx.ID_ANY)
self.txt = wx.TextCtrl(self.panel,id=wx.ID_ANY,pos=(185,40))
txtSizer = wx.BoxSizer(wx.HORIZONTAL)
self.btn = wx.Button(self.panel,id=wx.ID_ANY,label="Submit",pos=
(190,70),size=(100,30))
self.btn.Bind(wx.EVT_BUTTON,self.onSubmit)
def onSubmit(self,event):
gettxt = self.txt.GetValue()
I got the solution
def onBtn(self,event):
self.val = self.txtstring1.GetValue()
Blue = wx.Button(self,label="Blue",pos=(30,50))
Blue.Bind(wx.EVT_BUTTON,self.onBlue)
Green = wx.Button(self,label="Green",pos=(300,50))
Green.Bind(wx.EVT_BUTTON,self.onGreen)
for self.button_name in range(self.val):
self.btn = wx.Button(self, label=str(self.button_name),pos=(50,50))
self.btn.Bind(wx.EVT_BUTTON, lambda evt, temp=self.button_name:
self.OnButton(evt, temp))
self.widgetSizer.Add(self.btn, 0, wx.ALL|wx.CENTER, 5)

WxPython Exit Code 139 with Form Window

So this is a pretty basic program but for some reason it keeps crashing wtih exit code 139. I've looked online at the error code and it has to do with memory management but a basic form with a few plaintext, buttons, and fields should not be too much for a 64 bit machine with 16GB of RAM. I have another class that is almost identical and it works fine. Where am I going wrong here?
import wx
class mainForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Test")
self.panel = wx.Panel(self)
vbox_main = wx.BoxSizer(wx.VERTICAL) # main vertical box
url_box = wx.BoxSizer(wx.HORIZONTAL)
url_label = wx.StaticText(self.panel, label="URL:")
self.url_entry = wx.TextCtrl(self.panel)
url_box.Add(url_box)
url_box.Add(url_label)
url_box.Add(self.url_entry)
file_box = wx.BoxSizer(wx.HORIZONTAL)
file_label = wx.StaticText(self.panel, label="File")
self.file_entry = wx.TextCtrl(self.panel)
file_button = wx.Button(self.panel, label="Search")
file_button.Bind(wx.EVT_BUTTON, self.search)
file_box.Add(file_label)
file_box.Add(self.file_entry)
file_box.Add(file_button)
mode_box=wx.BoxSizer(wx.HORIZONTAL)
mode_label=wx.StaticText(self.panel, label='Mode')
#self.mode_button = buttons.GenToggleButton(self.panel, -1, "Autonomous Mode")
mode_box.Add(mode_label)
#mode_box.Add(self.mode_button)
go_box = wx.BoxSizer(wx.HORIZONTAL)
go_button = wx.Button(self.panel, label='Go!')
go_button.Bind(wx.EVT_BUTTON, self.submit)
go_box.Add(go_button)
vbox_main.Add(url_box)
vbox_main.Add(file_box)
vbox_main.Add(mode_box)
vbox_main.Add(go_box)
self.panel.SetSizer(vbox_main)
self.Show(True)
def search(self):
pass
def submit(self):
pass
You can not add wxBoxSizer himself and you need to comment the line:
url_box.Add(url_box)

wxPython: TextCtrl in pop up window

I have created a pop up window, but the TextCtrl is not fully expanded to fill up the window. It works great if I use StaticText instead, (but if content too large then I would need the scroll bar, that is why I am using TextCtrl now). Please provide some guidance.
self.description = WindowPopup(self, wx.SIMPLE_BORDER, content)
btn = event.GetEventObject()
dw = wx.DisplaySize()[0]
width = self.description.GetSize()[0]
y = btn.ClientToScreen((0,0))[1]
height = btn.GetSize()[1]
x = dw - width - 20 - 10
self.description.Position((x, y), (0, height))
self.description.Show(True)
class WindowPopup(wx.PopupWindow):
""" Pops up a window to provide description for the selection """
def __init__(self, parent, style, content):
wx.PopupWindow.__init__(self, parent, style)
self.SetSize((700, 287))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
st = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE | wx.TE_READONLY)
st.SetValue(content)
sizer.Add(st, 0, wx.EXPAND)
panel.SetSizer(sizer)
I suspect your problem is that the panel is not as big as the popupwindow ... so even though the textfield is expanding to fill its sizer area it is not filling the popup its self.
try using something like
def __init__(...):
...
self.SetMinSize((700,287))
sizer2 = wx.BoxSizer()
sizer2.Add(panel)
self.SetSizer(sizer2)
also make sure that you are calling layout on it at some point (note this is totally untested... so it may need some tweeks, or even worse just be wrong...)
The actual answer is:
sizer = wx.BoxSizer(wx.VERTICAL)
st = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE | wx.TE_READONLY, size = (500, 174))
st.SetValue(content)
self.SetSize((500, 174))
sizer.Add(st, 0, wx.EXPAND)
self.SetSizer(sizer)
self.Layout()
self.Show(True)
Credits to Joran for noticing Layout().
PopupWindow does not require an additional panel, because the window itself can have sizer set to it. This has been realized by using the wxPython Widget Inspection Tool.
Make sure TextCtrl and PopupWindow have the same size.

wxpython HtmlWindow: show full content?

How can I display the full content of a HtmlWindow? I have some HtmlWindows in a scrollable panel and I want to see the full text in these windows. I have tried setting the proportion to 1 and the style to wx.EXPAND, but that doesn't work.
Currently it looks like this:
But I want to see in the windows the full text:
some long text
with multiple lines
and another line
Sample code:
import wx
from wx import html
from wx.lib.scrolledpanel import ScrolledPanel
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.notebook_1 = wx.Notebook(self, -1, style=0)
self.notebook_1_pane_1 = ScrolledPanel(self.notebook_1, -1)
sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
sizer_2 = wx.BoxSizer(wx.VERTICAL)
for _ in xrange(10):
self.html = html.HtmlWindow(self.notebook_1_pane_1)
self.html.SetPage('some long text<br />with multiple lines<br />' \
'and another line')
self.html.SetBorders(0)
self.sizer_3_staticbox = wx.StaticBox(self.notebook_1_pane_1, -1,
'a')
sizer_3 = wx.StaticBoxSizer(self.sizer_3_staticbox, wx.VERTICAL)
sizer_3.Add(self.html, 1, wx.EXPAND, 0)
sizer_2.Add(sizer_3, 0, wx.EXPAND, 0)
self.notebook_1_pane_1.SetSizer(sizer_2)
self.notebook_1.AddPage(self.notebook_1_pane_1, "tab1")
sizer_1.Add(self.notebook_1, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
self.notebook_1_pane_1.SetScrollRate(20, 20)
if __name__ == "__main__":
app = wx.PySimpleApp()
frame_1 = MyFrame(None, -1, size=(400, 300))
app.SetTopWindow(frame_1)
frame_1.Show()
app.MainLoop()
You need to give sizer_2 a proportion of 1 or more as well.
sizer_2.Add(sizer_3, 1, wx.EXPAND, 0)
This makes the sizer_3 element stretch appropriately too. Otherwise, it only stretches in one direction. I would reduce the number of HTMLWindows you're putting in too unless you're using a high resolution monitor. Expanding this out so that all the text was visible in all the Windows is difficult on these low res wide screens.

how to make multiline wx.ListBox

I need a ListBox to show a phone book.Then i need to show name in top and number in bottom in each list item like phone.how to bind the datas into listbox.
now i made a listbox with singleline as shown below
cur.execute("select fname from tblsample1 order by fname")
names = [str(item[0]) for item in cur.fetchall()]
lvnames=wx.ListBox(panel,-1,(10,40),(210,180),names, wx.LB_SINGLE)
how to bind sqlite3 cursor with two columns to the listview
i need a wx.ListBox mouse click event(not EVT_LISTBOX
because i need only mouse click event)
Use the HtmlListBox, here is a little example to get you started.
import wx
class PhoneNumbers(wx.HtmlListBox):
def __init__(self, parent):
wx.HtmlListBox.__init__(self, parent)
self.data = [
("Foo", "3452-453"),
("Bar", "5672-346"),
]
self.SetItemCount(len(self.data))
def OnGetItem(self, n):
return "<b>%s</b><br>%s" % self.data[n]
def add_number(self, name, number):
self.data.append((name, number))
self.SetItemCount(len(self.data))
self.Refresh()
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, size=(200, 400))
self.numbers = PhoneNumbers(self)
self.contact_name = wx.TextCtrl(self)
self.contact_number = wx.TextCtrl(self)
self.add_btn = wx.Button(self, label="Add contact")
self.Sizer = wx.BoxSizer(wx.VERTICAL)
self.Sizer.Add(self.numbers, 1, wx.EXPAND)
self.Sizer.Add(wx.SearchCtrl(self), 0, wx.EXPAND)
self.Sizer.Add(wx.StaticText(self, label="Name"), 0, wx.TOP, 10)
self.Sizer.Add(self.contact_name)
self.Sizer.Add(wx.StaticText(self, label="Number"), 0, wx.TOP, 5)
self.Sizer.Add(self.contact_number)
self.Sizer.Add(self.add_btn, 0, wx.ALL, 10)
self.numbers.Bind(wx.EVT_LISTBOX, self.OnSelectNumber)
self.add_btn.Bind(wx.EVT_BUTTON, self.OnAddNumber)
def OnSelectNumber(self, event):
name, number = self.numbers.data[event.Selection]
self.contact_name.Value = name
self.contact_number.Value = number
def OnAddNumber(self, event):
self.numbers.add_number(
self.contact_name.Value,
self.contact_number.Value
)
app = wx.PySimpleApp()
app.TopWindow = f = Frame()
f.Show()
app.MainLoop()
You should rephrase your question, I don't know if I got this right.
If you only need to display the two lines in your ListBox, you could simply use a \n:
cur.execute("select fname,number from tblsample1 order by fname")
entries = [str(item[0])+'\n'+str(item[1]) for item in cur.fetchall()]
To get a 'click' Event, you cant set the style of your wx.ListBox to wx.LC_SINGLE_SEL and catch the selection event wx.EVT_LIST_ITEM_SELECTED

Categories

Resources