Is there a way to activate/deactivate the editability of an wxpython widget and especially the FilePickerCtrl widget?
In my case, I am creating a desktop app from which the user will be able to start and stop a server. Before starting the server the user can specify a variable through the FilePickerCtrl widget and this variable will be used from the server. I want to make that FilePickerCtrl editable while the server is stopped and un-editable while the server is running.
So far I have tried the wxpython Validators without success. Some reproducable code:
configuration.py (you need to edit the excel path)
global SERVER_CONF
SERVER_CONF = {
'XE_EXCEL_PATH': '/absolute/path/to/excel.xlsx'
}
gui_main.py
import wx
from server_tab import Server
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="")
# Create a panel and notebook (tabs holder)
p = wx.Panel(self)
nb = wx.Notebook(p)
# Create the tab windows
tab1 = Server(nb)
# Add the windows to tabs and name them.
nb.AddPage(tab1, tab1.name)
# Set noteboook in a sizer to create the layout
sizer = wx.BoxSizer()
sizer.Add(nb, 1, wx.EXPAND)
p.SetSizer(sizer)
if __name__ == "__main__":
app = wx.App()
MainFrame().Show()
app.MainLoop()
server.py
from flask import request
from flask import Flask
from configuration import SERVER_CONF
app = Flask(__name__)
#app.route('/', methods=['POST'])
def read_html_source():
global SERVER_CONF
print('HEREEE',SERVER_CONF['XE_EXCEL_PATH'])
return ('', 200)
#app.route("/shutdown", methods=['GET'])
def shutdown():
shutdown_func = request.environ.get('werkzeug.server.shutdown')
if shutdown_func is None:
raise RuntimeError('Not running werkzeug')
shutdown_func()
return ("Shutting down...", 200)
def main():
app.run(debug=False, port=5001)
if __name__ == "__main__":
main()
server_tab.py
import os
import requests
import threading
import wx
import server
from configuration import SERVER_CONF
# Define the tab content as classes:
class Server(wx.Panel):
name = "Server"
def __init__(self, parent):
global SERVER_CONF
self.is_server_active = False
wx.Panel.__init__(self, parent)
generic_sizer = wx.BoxSizer(wx.VERTICAL)
title = wx.StaticText(self, 0, "The APE API server")
generic_sizer.Add(title, 0, wx.ALL | wx.EXPAND, 5)
file_sizer = wx.BoxSizer(wx.HORIZONTAL)
excel_label = wx.StaticText(self, 0, "Excel ")
file_sizer.Add(excel_label, 0, wx.ALL | wx.EXPAND, 5)
self. excel_browser = wx.FilePickerCtrl(
self,
wildcard='*.xlsx',
path=SERVER_CONF['XE_EXCEL_PATH'],
validator=ServerValidator(self.is_server_active)
)
file_sizer.Add(self.excel_browser, 0, wx.ALL , 5)
generic_sizer.Add(file_sizer, 0, wx.ALL , 5)
server_btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
start_server_btn = wx.Button(self, label='Run Server')
start_server_btn.Bind(wx.EVT_BUTTON, self.start_server)
server_btn_sizer.Add(start_server_btn, 0, wx.ALL | wx.EXPAND, 5)
stop_server_btn = wx.Button(self, label='Stop Server')
stop_server_btn.Bind(wx.EVT_BUTTON, self.stop_server)
server_btn_sizer.Add(stop_server_btn, 0, wx.ALL | wx.EXPAND, 5)
generic_sizer.Add(server_btn_sizer, 0, wx.ALL | wx.EXPAND, 5)
parent.SetSizer(generic_sizer)
def start_server(self, entry):
SERVER_CONF['XE_EXCEL_PATH'] = self.excel_browser.GetPath()
self.is_server_active = True
self.thread = threading.Thread(target=server.main)
self.thread.start()
def stop_server(self, entry):
resp = requests.get('http://localhost:5001/shutdown')
self.thread.join()
print('Server was stoped')
class ServerValidator(wx.PyValidator):
def __init__(self, is_server_active):
wx.Validator.__init__(self)
self.is_server_active = is_server_active
def Clone(self):
'''Required Validator method'''
return ServerValidator(self.is_server_active)
def Validate(self, win):
window = self.GetWindow()
print("Is running?", self.is_server_active)
return not self.is_server_active
Run with python gui_main.py and test with curl http://localhost:5001 -d POST. In case you forget to stop the server from the GUI run curl http://localhost:5001/shutdown
The simplest way is to Enable and Disable the control. This works generally on wx controls
import wx
class MainFrame(wx.Frame):
"""Create MainFrame class."""
def __init__(self, *args, **kwargs):
super().__init__(None, *args, **kwargs)
self.size = (400, 1000)
self.title_text = 'Wx App'
self.panel = MainPanel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.panel)
self.SetSizer(sizer)
self.Size = (600, 200)
self.Center()
self.Show()
def on_disable(self, event):
self.panel.excel_browser.Disable()
def on_enable(self, event):
self.panel.excel_browser.Enable()
class MainPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.excel_browser = wx.FilePickerCtrl(self)
btn_disable = wx.Button(self, id=wx.ID_CANCEL)
btn_disable.Bind(wx.EVT_BUTTON, parent.on_disable)
btn_enable = wx.Button(self, id=wx.ID_OK)
btn_enable.Bind(wx.EVT_BUTTON, parent.on_enable)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.excel_browser)
sizer.Add(btn_disable)
sizer.Add(btn_enable)
self.SetSizer(sizer)
if __name__ == '__main__':
wx_app = wx.App()
MainFrame()
wx_app.MainLoop()
Just to prove Psionman's point about Enabling and Disabling wx widgets and that you can also test if a widget IsEnabled or not, which can be a useful tool, especially for toggling a control.
Here, the FilePickerCtrl and the Lock button are toggled between Enabled and Disabled and the button label is toggled, based on whether the Picker is Enabled or not.
import wx
class MainWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Lockable File Selector', size=(400, 200))
self.panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
self.s_box = wx.StaticBox(self.panel, -1, 'Please select the input file')
s_sizer = wx.StaticBoxSizer(self.s_box, wx.VERTICAL)
fp_box = wx.BoxSizer(wx.HORIZONTAL)
self.fp_ctrl = wx.FilePickerCtrl(self.panel, wx.ID_ANY, message="Choose a file")
self.fp_lock = wx.Button(self.panel, wx.ID_ANY, label="Lock")
fp_box.Add(self.fp_ctrl, 1, wx.ALL | wx.CENTER | wx.EXPAND, 5)
fp_box.Add(self.fp_lock, 0, wx.ALL | wx.CENTER, 5)
s_sizer.Add(fp_box, 1, wx.ALL | wx.CENTER | wx.EXPAND, 10)
vbox.Add(s_sizer, 0, wx.ALL | wx.CENTER | wx.EXPAND, 5)
self.panel.SetSizer(vbox)
self.fp_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED, self.on_open_file)
self.fp_lock.Bind(wx.EVT_BUTTON, self.on_lock)
self.panel.Fit()
self.fp_lock.Enable(False)
self.Show()
def on_open_file(self, event):
path = self.fp_ctrl.GetPath()
use_path, x = path.rsplit('/', 1)
self.s_box.SetLabel(use_path)
if path:
self.fp_lock.Enable(True)
def on_lock(self, event):
if self.fp_ctrl.IsEnabled():
self.fp_ctrl.Enable(False)
self.fp_lock.SetLabel('Unlock')
else:
self.fp_ctrl.Enable(True)
self.fp_lock.SetLabel('Lock')
if __name__ == '__main__':
app = wx.App()
frame = MainWindow()
app.MainLoop()
Related
I am trying to get a variable out of wxPython file open dialog. I have 2 button which gets the path to 2 files with .GetPath() ?
this is the code I have so far
'def onclk1(event):
with wx.FileDialog(panel, "OPEN EMG FILE", wildcard="TXT Files(*.txt)|*.txt",
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as EmgFile:
if EmgFile.ShowModal() == wx.ID_CANCEL:
return "cancelled"
emg = EmgFile.GetPath()
e1.Bind(wx.EVT_BUTTON, onclk1)
Now I need to pass the path outside def to another variable.
Thanks in advance
In future it might be better to provide a minimal working example of your code.
Don't forget that a GUI is event driven and you must use the event to assign the return value of a dialog to the variable you want.
You've not said what you want to do with the file path, but this code shows how to assign it to a label.
"""Main Frame module for basic wxPython App."""
import wx
class MainFrame(wx.Frame):
"""Create MainFrame class."""
def __init__(self, *args, **kwargs):
super().__init__(None, *args, **kwargs)
self.size = (400, 1000)
self.Title = 'wx App'
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 onclk1(self, event):
with wx.FileDialog(self.panel, "OPEN EMG FILE", wildcard="TXT Files(*.txt)|*.txt",
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as EmgFile:
if EmgFile.ShowModal() == wx.ID_CANCEL:
return "cancelled"
emg = EmgFile.GetPath()
self.panel.lbl_file1.SetLabel(emg)
def on_quit_click(self, event):
"""Handle close 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)
e1 = wx.Button(self, label='File1')
e1.Bind(wx.EVT_BUTTON, parent.onclk1)
self.lbl_file1 = wx.StaticText(self, label=' '*100)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(e1)
sizer.Add(self.lbl_file1)
self.SetSizer(sizer)
if __name__ == '__main__':
wx_app = wx.App()
MainFrame()
wx_app.MainLoop()
Just to provide another option, there is also a wx.FilePickerCtrl
import wx
import os
wildcard = "All files (*.*)|*.*"
class MainWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='File Selector')
self.currentDirectory = os.getcwd()
self.panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
ie_box = wx.StaticBox(self.panel, -1, 'Please select the input file')
ie_sizer = wx.StaticBoxSizer(ie_box, wx.VERTICAL)
fl_box = wx.BoxSizer(wx.HORIZONTAL)
self.fl_ctrl = wx.FilePickerCtrl(self.panel, message="Choose a file")
fl_box.Add(self.fl_ctrl, 1, wx.ALL | wx.CENTER | wx.EXPAND, 5)
ie_sizer.Add(fl_box, 1, wx.ALL | wx.CENTER | wx.EXPAND, 10)
self.fl_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED, self.on_open_file)
vbox.Add(ie_sizer, 0, wx.ALL | wx.CENTER | wx.EXPAND, 5)
self.panel.SetSizer(vbox)
self.Center()
self.panel.Fit()
self.Show()
def on_open_file(self, event):
self.fl_ctrl.GetPath()
if __name__ == '__main__':
app = wx.App()
frame = MainWindow()
app.MainLoop()
I would like to assign the value of a menu click to a class variable so I can use that value throughout multiple classes. Any help would be greatly appreciated!
import wx
class TrackPanel (wx.Panel):
""""""
def __init__(self, parent, *args, **kwargs):
"""Constructor"""
wx.Panel.__init__(self, parent, *args, **kwargs)
mainSizer =wx.BoxSizer(wx.VERTICAL)
track = MasterPage.track_selected # This is where i would like access the variable
title = wx.StaticText(self, wx.ID_ANY, label = track)
mainSizer.Add(title, 0,wx.ALL | wx.EXPAND | wx.CENTER)
self.SetSizerAndFit(mainSizer)
self.Layout()
class MasterPage (wx.Frame):
track_selected = ' '
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
menubar = wx.MenuBar()
tracksopt = wx.Menu()
track_command = wx.MenuItem(tracksopt, wx.ID_ANY, 'Atlanta')
tracksopt.Append(track_command)
self.Bind(wx.EVT_MENU, self.change_track, track_command)
track_command2 = wx.MenuItem(tracksopt, wx.ID_ANY, 'Texas')
tracksopt.Append(track_command2)
self.Bind(wx.EVT_MENU, self.change_track, track_command2)
menubar.Append(tracksopt, '&Tracks')
self.SetMenuBar(menubar)
def change_track (self, event):
"""Changes the tack_selected variable based on the menu item picked"""
id_selected = event.GetId()
obj = event.GetEventObject()
track_id= obj.GetLabel(id_selected)
MasterPage.track_selected = track_id
You must create a link to the master in the tracker in order for the tracker to access values in the master.
import wx
class Track (wx.Frame):
def __init__(self, link):
wx.Frame.__init__(self, None)
self.link = link
self.tc = wx.TextCtrl(self, style=wx.TE_MULTILINE)
self.bt = wx.Button(self, label="tracker (push to read master)")
self.bt.Bind(wx.EVT_BUTTON, self.on_read_master)
self.Title = 'Tracker'
sz_1 = wx.BoxSizer(wx.HORIZONTAL)
sz_2 = wx.BoxSizer(wx.VERTICAL)
sz_2.Add(self.tc, 1, wx.EXPAND, 0)
sz_2.Add(self.bt, 0, wx.EXPAND, 0)
sz_1.Add(sz_2, 1, wx.EXPAND, 0)
self.SetSizer(sz_1)
self.Layout()
self.SetSize((250, 100))
def on_read_master(self, evt):
self.tc.SetValue(self.link.track_selected) # here you access the variable)
class Master(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.track_selected = '0'
self.tc = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE)
self.bt = wx.Button(self, wx.ID_ANY, "master (push to send)")
self.bt.Bind(wx.EVT_BUTTON, self.change_track)
self.Title = 'Master'
self.tc.SetValue('enter value to be read by the tracker')
sz_1 = wx.BoxSizer(wx.HORIZONTAL)
sz_2 = wx.BoxSizer(wx.VERTICAL)
sz_2.Add(self.tc, 1, wx.EXPAND, 0)
sz_2.Add(self.bt, 0, wx.EXPAND, 0)
sz_1.Add(sz_2, 1, wx.EXPAND, 0)
self.SetSizer(sz_1)
self.Layout()
self.SetSize((250, 100))
def change_track (self, evt):
"""Changes variable based on text entered"""
self.track_selected = self.tc.GetValue()
if __name__ == "__main__":
app = wx.App(0)
master = Master()
track = Track(master)
master.Show()
track.Show()
app.MainLoop()
This is the fast and dirty method I generally use. If you want something more sophisticated, there is a dedicated library to pass messages between frames: pubsub. A good tutorial of how to use it can be found in the Mike Driscoll blog
I have a problem: I want to make a password manager for myself .
I have the logic for the en/decoding done and also I have the dynamic adding of passwords done, but if I add too many passwords to my manager then I can't see all of the passwords because my screen is too small.
I wanted to add a scroll bar but the scroll bar didn't work.
Even after several hours of researching the code didn't want to work. This is the code for the PanelTwo:
class PanelTwo(wx.Panel):
#staticmethod
def scale_bitmap(bitmap, width, height):
image = wx.ImageFromBitmap(bitmap)
image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
result = wx.BitmapFromImage(image)
return result
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
self.refr = wx.Button(self,wx.ID_ANY,u"Refresh")
self.refr.Bind(wx.EVT_BUTTON, self.refresh)
self.co = wx.Button(self, wx.ID_ANY, u"Close")
self.co.Bind(wx.EVT_BUTTON, self.close)
self.number_of_pwds = 0
self.frame = parent
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
controlSizer = wx.BoxSizer(wx.HORIZONTAL)
self.widgetSizer = wx.BoxSizer(wx.VERTICAL)
#controlSizer.Add(self.Passest, 0, wx.CENTER | wx.ALL, 5)
controlSizer.Add(self.refr, 0, wx.CENTER | wx.ALL, 5)
controlSizer.Add(self.co, 0, wx.CENTER | wx.ALL, 5)
self.addButton = wx.Button(self, label="Add")
self.addButton.Bind(wx.EVT_BUTTON, self.onAddWidget)
controlSizer.Add(self.addButton, 0, wx.CENTER | wx.ALL, 5)
self.removeButton = wx.Button(self, label="Remove")
self.removeButton.Bind(wx.EVT_BUTTON, self.onRemoveWidget)
controlSizer.Add(self.removeButton, 0, wx.CENTER | wx.ALL, 5)
self.mainSizer.Add(controlSizer, 0, wx.CENTER)
self.mainSizer.Add(self.widgetSizer, 0, wx.CENTER | wx.ALL, 10)
self.SetSizer(self.mainSizer)
The adding and removing of the Pwds is here :
def onAddWidget(self, event):
self.number_of_pwds += 1
label = "Pwd %s" % self.number_of_pwds
name = "Pwd%s" % self.number_of_pwds
new_Text = wx.StaticText(self, label=label, name=name)
self.widgetSizer.Add(new_Text, 0, wx.ALL, 5)
self.frame.fSizer.Layout()
self.frame.Fit()
def onRemoveWidget(self, event):
if self.widgetSizer.GetChildren():
self.widgetSizer.Hide(self.number_of_pwds - 1)
self.widgetSizer.Remove(self.number_of_pwds - 1)
self.number_of_pwds -= 1
self.frame.fSizer.Layout()
self.frame.Fit()
my main Form is here :
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"Passwort Manager",
size=(300,130))
self.panel_one = PanelOne(self)
self.panel_two = PanelTwo(self)
self.panel_three = PanelThree(self)
self.panel_two.Hide()
self.panel_three.Hide()
self.fSizer = wx.BoxSizer(wx.VERTICAL)
self.fSizer.Add(self.panel_one, 1, wx.EXPAND)
self.fSizer.Add(self.panel_two, 1, wx.EXPAND)
self.fSizer.Add(self.panel_three,1,wx.EXPAND)
self.SetSizer(self.fSizer)
self.SetBackgroundColour(Color.White)
def onSwitchPanels(self, event):
if self.panel_one.IsShown():
self.SetTitle("Passwort Manager")
self.SetBackgroundColour(Color.Random())
self.panel_one.Hide()
self.panel_two.Show()
elif self.panel_two.IsShown():
self.SetTitle("Passwort Manager")
self.SetBackgroundColour(Color.Random())
self.panel_three.Show()
self.panel_two.Hide()
else:
self.SetTitle("Passwort Manager")
self.SetBackgroundColour(Color.Random())
self.panel_one.Show()
self.panel_three.Hide()
self.Layout()
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
So how can I add a dynamic scrollbar that resizes automatically and that works with my code? I am using Python3.6.
Thanks for helping me out.
Have you tried using a scrolled panel?
import wx
import wx.lib.scrolledpanel as scrolled
class TestPanel(scrolled.ScrolledPanel):
def __init__(self, parent):
scrolled.ScrolledPanel.__init__(self, parent, -1)
"""
Write your code here...
"""
self.SetupScrolling()
New python programmer here and trying to learn how to dynamically update widgets. To start, I have the following code. What I would like to do is change my variable "self.dynamiclength" to any integer, and have WxPython update the number of widgets appropriately. I have tried putting self.Refresh() and self.Update() in my TestFrame after updating self.dynamiclength to no avail.
I have done as much reading as possible on this before resorting to asking for help, but I am just too new at Wx to solve this one on my own. Thank you much!
import wx
import wx.lib.scrolledpanel as scrolled
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, size=(1000, 550))
panel = wx.Panel(self)
mainSizer = wx.BoxSizer(wx.VERTICAL)
pbox0 = wx.BoxSizer(wx.VERTICAL)
controlback0 = wx.Button(panel, label="Back0")
controlforward0 = wx.Button(panel, label="Forward0")
pbox0.Add(controlback0, 0, wx.ALL)
pbox0.Add(controlforward0, 0, wx.ALL)
mainSizer.Add(pbox0)
self.scrolling_window = scrolled.ScrolledPanel( panel )
self.scrolling_window.SetAutoLayout(1)
self.scrolling_window.SetupScrolling()
self.sizer = wx.BoxSizer( wx.VERTICAL )
self.child_windows = []
##############################################
#this is the variable that I want to change,
#and I don't know how to get the 'for loop'
#below to update as well.
self.eedictionary = {}
self.dynamiclength = 5
for i in range(0,self.dynamiclength):
wind = self.addBox(i)
self.sizer.Add(wind, 0, wx.CENTER|wx.ALL, 5)
###############################################
#the following code binds all appropriate buttons to a pedigree variable updater
button_binding_list = ['controlback','controlforward']
for j in button_binding_list:
eid = self.eedictionary[str(i)+j]
self.scrolling_window.Bind(wx.EVT_BUTTON, lambda evt: self.onclick(evt, id), id=eid)
self.scrolling_window.SetSizer(self.sizer)
mainSizer.Add(self.scrolling_window, 1, wx.EXPAND)
panel.SetSizer(mainSizer)
def addBox(self, i):
pbox = wx.BoxSizer(wx.VERTICAL)
controlback = wx.Button(self.scrolling_window, label="Back")
controlforward = wx.Button(self.scrolling_window, label="Forward")
pbox.AddMany([(controlback, 0, wx.ALL), (controlforward, 0, wx.ALL)])
#for each object created in the addBox module, its id is added to the dictionary
self.eedictionary[str(i)+'controlback'] = controlback.GetId()
self.eedictionary[str(i)+'controlforward'] = controlforward.GetId()
return pbox
def onclick(self, event):
self.dynamiclength +=1
print 'added one to self.dynamiclength', self.dynamiclength
if __name__=='__main__':
app = wx.App(False)
f = TestFrame()
f.Show()
app.MainLoop()
I have similar test code which I have written some time ago. Maybe you will find it useful.
import wx
#===================================================================================================
class UpperPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.combo = wx.ComboBox(self, choices=["0", "1", "2", "3", "4"], size=(200, -1))
self.combo.Bind(wx.EVT_COMBOBOX, self.GetParent().middlePanel.Change)
self.logo = wx.Button(self, size=(300, 100))
self.sizer = wx.BoxSizer()
self.sizer.Add(self.combo, 0, wx.EXPAND)
self.sizer.Add(self.logo, 0, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
#===================================================================================================
class MiddlePanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.subs = []
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizerAndFit(self.sizer)
def Change(self, e):
self.sizer = wx.BoxSizer(wx.VERTICAL)
for a in self.subs:
a.Destroy()
self.subs = []
for a in range(int(e.GetString())):
b = wx.Button(self, size=(-1, 50))
self.subs.append(b)
self.sizer.Add(b, 1, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
self.GetParent().Fit()
#===================================================================================================
class MainWin(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.middlePanel = MiddlePanel(self)
self.upperPanel = UpperPanel(self)
self.textArea = wx.TextCtrl(self, size=(-1, 300), style=wx.TE_MULTILINE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.upperPanel, 0, wx.EXPAND)
self.sizer.Add(self.middlePanel, 0, wx.EXPAND)
self.sizer.Add(self.textArea, 1, wx.EXPAND)
self.SetSizerAndFit(self.sizer)
#===================================================================================================
if __name__ == '__main__':
app = wx.PySimpleApp()
main_win = MainWin()
main_win.Show()
app.MainLoop()
If you need to update the number of widgets AFTER you've already created and shown the application, the you'll need to do it in a method, NOT in the init. The init only runs the first time the application is instantiated. Whenever you add or remove widgets after the frame is shown, you'll need to call Layout() on the parent widget or its sizer. See also
http://wxpython-users.1045709.n5.nabble.com/dynamically-adding-amp-removing-widgets-td2342432.html
https://groups.google.com/forum/?fromgroups#!topic/wxPython-users/eQjlYlsw4qs
Adding a widget with a button - wxPython
i am having an interesting problem,
This program is a simple image viewer and it can contain different images in a listbox. The listbox contains the names of the images. You can load an image to an item in the listbox. You can click any item on the listbox to see its image. For some reason Destroy() is not functioning properly. Please run the following code if you are unable to understand me,
IMAGE_NAME=[]
IMAGE_DATA=[]
IMAGE_LISTSEL=[]
import sys
import wx
def deletepic(self,parent):
try:
bitmap1.Destroy()
bmp1.Destroy()
except:
print sys.exc_info()
def sendnewpic(self,parent):
global scroll_img
deletepic(self,parent)
print IMAGE_DATA[IMAGE_LISTSEL[0]]
if IMAGE_DATA[IMAGE_LISTSEL[0]]!='':
try:
print IMAGE_DATA[IMAGE_LISTSEL[0]]
bmp1 = wx.Image(IMAGE_DATA[IMAGE_LISTSEL[0]], wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bitmap1 = wx.StaticBitmap(scroll_img, -1, bmp1, (0, 0))
except:
pass
def areachange(self,pg):
print pg
try:
if IMAGE_DATA[IMAGE_LISTSEL[0]]=='':
deletepic(self,parent)
except:
pass
if pg=="Images":
self.images_area.Show()
else:
self.images_area.Hide()
class imageMax(wx.Panel):
pass
class imageTab(imageMax):
def imagesel(self,parent):
IMAGE_LISTSEL[:] = []
IMAGE_LISTSEL.append(self.listBox.GetSelection())
sendnewpic(self,parent)
def newAddImage(self,parent):
IMAGE_NAME.append('hi');
IMAGE_DATA.append('');
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(len(IMAGE_NAME)-1)
self.imagesel(self) #making it a selected image, globally
def reName(self,parent):
sel = self.listBox.GetSelection()
text = self.listBox.GetString(sel)
renamed = wx.GetTextFromUser('Rename item', 'Rename dialog', text)
if renamed != '':
IMAGE_NAME.pop(sel)
IMAGE_NAME.insert(sel,renamed)
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(sel)
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.listBox = wx.ListBox(self, size=(200, -1), choices=IMAGE_NAME, style=wx.LB_SINGLE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.VERTICAL) #change to horizontal for side by side
self.sizerMain = wx.BoxSizer()
self.listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.reName)
self.listBox.Bind(wx.EVT_LISTBOX, self.imagesel)
btn = wx.Button(self, label="Create New",size=(200, 40))
btnTwo = wx.Button(self, label="Test 2",size=(200, 40))
btn.Bind(wx.EVT_BUTTON, self.newAddImage)
self.sizer.Add(self.listBox, proportion=1, flag=wx.TOP | wx.EXPAND | wx.LEFT, border=5)
btnSizer.Add(btn, 0, wx.ALL, 5)
btnSizer.Add(btnTwo, 0, wx.ALL, 5)
self.sizer.Add(btnSizer)
self.sizerMain.Add(self.sizer, proportion=0, flag=wx.BOTTOM | wx.EXPAND, border=0)
self.SetSizer(self.sizerMain)
class MyNotebook(wx.Notebook):
def __init__(self, *args, **kwargs):
wx.Notebook.__init__(self, *args, **kwargs)
class MyPanel(imageTab):
def OnClickTop(self, event):
scroll_img.Scroll(600, 400)
def OnClickBottom(self, event):
scroll_img.Scroll(1, 1)
def OnPageChanged(self, event):
new = event.GetSelection()
areachange(self,self.notebook.GetPageText(new))
event.Skip()
def OnPageChanging(self, event):
event.Skip()
def onOpenFile(self,parent):
""" Open a file"""
filename = wx.FileSelector()
if (filename!=''):
global bitmap1,bmp1,scroll_img
if IMAGE_DATA[IMAGE_LISTSEL[0]]!='':
deletepic(self,parent)
bmp1 = wx.Image(filename, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bitmap1 = wx.StaticBitmap(scroll_img, -1, bmp1, (0, 0))
scroll_img.SetScrollbars(1, 1, bmp1.GetWidth(), bmp1.GetHeight())
IMAGE_DATA[IMAGE_LISTSEL[0]]=filename
print IMAGE_DATA
def __init__(self, *args, **kwargs):
global bitmap1,bmp1,scroll_img
wx.Panel.__init__(self, *args, **kwargs)
self.notebook = MyNotebook(self, size=(225, -1))
# self.button = wx.Button(self, label="Something else here? Maybe!")
tab_images = imageTab(self.notebook)
# add the pages to the notebook with the label to show on the tab
self.notebook.AddPage(tab_images, "Pics",select=True)
scroll_img = wx.ScrolledWindow(self, -1)
scroll_img.SetScrollbars(1, 1, 600, 400)
#self.button = wx.Button(scroll_img, -1, "Scroll Me", pos=(50, 20))
#self.Bind(wx.EVT_BUTTON, self.OnClickTop, self.button)
#self.button2 = wx.Button(scroll_img, -1, "Scroll Back", pos=(500, 350))
#self.Bind(wx.EVT_BUTTON, self.OnClickBottom, self.button2)
self.images_area=wx.StaticBox(self, -1, '')
self.sizerBox = wx.StaticBoxSizer(self.images_area,wx.HORIZONTAL)
#self.load_file=wx.Button(self, label='Load File')
#self.sizerBox.Add(self.load_file,0,wx.ALL,5)
self.sizerBox2 = wx.BoxSizer()
self.sizerBox.Add(scroll_img, 1, wx.EXPAND|wx.ALL, 10)
self.sizerBox2.Add(self.sizerBox, 1, wx.EXPAND|wx.ALL, 10)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
# self.sizer.Add(self.button, proportion=0)
btnSizer = wx.BoxSizer() #change to horizontal for side by side
btnTwo = wx.Button(self, label="Load File",size=(200, 40))
btnTwo.Bind(wx.EVT_BUTTON,self.onOpenFile)
bmp1 = None
bitmap1 = None
btnSizer.Add(btnTwo, 0, wx.TOP, 15)
self.sizerBox2.Add(btnSizer)
#self.sizerBox.Add(self.bitmap1)
self.sizer.Add(self.sizerBox2, proportion=1, flag=wx.EXPAND)
self.SetSizer(self.sizer)
self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
areachange(self,self.notebook.GetPageText(0))
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = MyPanel(self)
self.Show()
app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()
Try this to see what the error is,
1.you press create new (any amount of times)
2.press, load file
3. click any item on the listbox (except for the one that you are in)
4. Then go back to the orginal item that you were in,
5. Then click any item, other than the one you are currently in
There will be some sort of problem, the image does not destroy itself and returns an error like the following:
(, PyDeadObjectError('The C++ part of the StaticBitmap object has been deleted, attribute access no longer allowed.',), )
I am still able to load images but the previous images do not delete.
It is hard to word this problem, if anyone can help me with this situation, it would be greatly appreciated. If you need further explanation please comment. I thank you greatly for viewing.
Here you have your code fixed to clear your current image when loading another one.
This is done basically using self.parent.bitmap.Destroy().
I modified some few things without changing the structure of your code, in order for you to recognize changes. I eliminated globals calls. Look how I also eliminated the global IMAGE_LISTSEL variable and converted it in a class attribute. That is what Robin and Fenikso were telling you. Try to do the same with IMAGE_NAME and IMAGE_DATA.
Although the code is working, it is still far from being acceptable wxpython code. You can get many examples of correctly written wxpython code in the web. If you can afford it I recommend to you wxPython in Action from Noel Rappin and Robin Dunn.
IMAGE_NAME = []
IMAGE_DATA = []
import sys
import wx
def deletepic(self):
try:
self.parent.bitmap.Destroy()
except:
print sys.exc_info()
def sendnewpic(self):
if self.parent.bitmap: deletepic(self)
if IMAGE_DATA[self.image_listsel] != '':
try:
print IMAGE_DATA[self.image_listsel]
bmp = wx.Image(IMAGE_DATA[self.image_listsel], wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.parent.scroll_img.SetScrollbars(1, 1, bmp.GetWidth(), bmp.GetHeight())
self.parent.bitmap = wx.StaticBitmap(self.parent.scroll_img, -1, bmp, (0, 0))
self.parent.Refresh()
except:
pass
def areachange(self, pg):
print pg
try:
if IMAGE_DATA[self.image_listsel] == '':
deletepic(self)
except:
pass
if pg == "Images":
self.images_area.Show()
else:
self.images_area.Hide()
class imageTab(wx.Panel):
def __init__(self, parent, grandparent):
wx.Panel.__init__(self, parent)
self.parent = grandparent
self.image_listsel = 0
self.listBox = wx.ListBox(self, size=(200, -1), choices=IMAGE_NAME, style=wx.LB_SINGLE)
self.sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.VERTICAL) #change to horizontal for side by side
self.sizerMain = wx.BoxSizer()
self.listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.reName)
self.listBox.Bind(wx.EVT_LISTBOX, self.imagesel)
btn = wx.Button(self, label="Create New",size=(200, 40))
btnTwo = wx.Button(self, label="Test 2",size=(200, 40))
btn.Bind(wx.EVT_BUTTON, self.newAddImage)
self.sizer.Add(self.listBox, proportion=1, flag=wx.TOP | wx.EXPAND | wx.LEFT, border=5)
btnSizer.Add(btn, 0, wx.ALL, 5)
btnSizer.Add(btnTwo, 0, wx.ALL, 5)
self.sizer.Add(btnSizer)
self.sizerMain.Add(self.sizer, proportion=0, flag=wx.BOTTOM | wx.EXPAND, border=0)
self.SetSizer(self.sizerMain)
def imagesel(self, evt):
self.image_listsel = self.listBox.GetSelection()
sendnewpic(self)
def newAddImage(self, evt):
IMAGE_NAME.append('hi')
IMAGE_DATA.append('')
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(len(IMAGE_NAME)-1)
self.imagesel(None) #making it a selected image, globally
def reName(self,parent):
sel = self.listBox.GetSelection()
text = self.listBox.GetString(sel)
renamed = wx.GetTextFromUser('Rename item', 'Rename dialog', text)
if renamed != '':
IMAGE_NAME.pop(sel)
IMAGE_NAME.insert(sel,renamed)
self.listBox.Set(IMAGE_NAME)
self.listBox.SetSelection(sel)
class MyPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
self.notebook = wx.Notebook(self, size=(225, -1))
#
self.tab_images = imageTab(self.notebook, self)
# add the pages to the notebook with the label to show on the tab
self.notebook.AddPage(self.tab_images, "Pics", select=True)
self.scroll_img = wx.ScrolledWindow(self, -1)
self.scroll_img.SetScrollbars(1, 1, 600, 400)
self.images_area = wx.StaticBox(self, -1, '')
self.sizerBox = wx.StaticBoxSizer(self.images_area, wx.HORIZONTAL)
self.sizerBox2 = wx.BoxSizer()
self.sizerBox.Add(self.scroll_img, 1, wx.EXPAND|wx.ALL, 10)
self.sizerBox2.Add(self.sizerBox, 1, wx.EXPAND|wx.ALL, 10)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.notebook, proportion=0, flag=wx.EXPAND)
#
btnSizer = wx.BoxSizer() #change to horizontal for side by side
btnTwo = wx.Button(self, label="Load File", size=(200, 40))
btnTwo.Bind(wx.EVT_BUTTON, self.onOpenFile)
self.bmp = None
self.bitmap = None
btnSizer.Add(btnTwo, 0, wx.TOP, 15)
self.sizerBox2.Add(btnSizer)
#
self.sizer.Add(self.sizerBox2, proportion=1, flag=wx.EXPAND)
self.SetSizer(self.sizer)
self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
areachange(self, self.notebook.GetPageText(0))
def OnClickTop(self, event):
self.scroll_img.Scroll(600, 400)
def OnClickBottom(self, event):
self.scroll_img.Scroll(1, 1)
def OnPageChanged(self, event):
new = event.GetSelection()
areachange(self, self.notebook.GetPageText(new))
event.Skip()
def OnPageChanging(self, event):
event.Skip()
def onOpenFile(self, evt):
""" Open a file"""
filename = wx.FileSelector()
if filename != '':
IMAGE_DATA[ self.tab_images.image_listsel] = filename
self.tab_images.imagesel(None)
print IMAGE_DATA
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = MyPanel(self)
self.Show()
app = wx.App(False)
win = MainWindow(None, size=(600, 400))
app.MainLoop()
Sometimes you are using bmp1 and bitmap1 as local variables and sometimes as globals. Since you are making multiple instances of them without saving the prior references anywhere then you are losing your references to the already existing objects. When you Destroy() them then you are only destroying the most recently created instances.
Try adding them to some sort of collection (like a list) instead and then you can access any of the items from the list when you need them later. Also try to avoid using global variables. Store your variables in the object instances that they belong to.