wxPython CollapsiblePane strange clipping issue - python

This is a follow up from allocating more size in sizer to wx.CollapsiblePane when expanded.
EDIT: The answer to that question solved my original problem, which was that nothing moved when I expanded or collapsed a pane, but now I've encountered another bug. While things do move properly, the buttons seem to smear out over each other as shown in the image below. Mousing over a button seems to force it to redraw properly over the rest of the mess, but it leaves a bunch of random button pieces drawn behind and around it.
I've done my absolute best to replicate this bug in a sample app, but I can't. I'm just looking for leads here. Does anyone have any idea what might cause the kind of issue shown below? It only happens after expanding and collapsing some of the upper panes.
For what it's worth, code below is for a sample app that looks very similar, but for some reason doesn't cause the same problems. EDIT Also for what it's worth, here's a link to the full source code for the GUI of my project which generated the screenshot below. http://code.google.com/p/dicom-sr-qi/source/browse/gui/main.py?r=8a876f7b4a034df9747a2c1f2791258f671e44b1
import wx
class SampleSubPanel(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.StaticText(self, 1, "A label"))
sizer.Add(wx.SpinCtrl(self))
self.SetSizer(sizer)
class SampleCollapsiblePane(wx.CollapsiblePane):
def __init__(self, *args, **kwargs):
wx.CollapsiblePane.__init__(self,*args,**kwargs)
sizer = wx.BoxSizer(wx.VERTICAL)
for x in range(2):
sizer.Add(wx.CheckBox(self.GetPane(), label = str(x)))
sizer.Add(SampleSubPanel(self.GetPane()))
self.GetPane().SetSizer(sizer)
self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.on_change)
def on_change(self, event):
self.GetParent().Layout()
class SampleSubPanel2(wx.Panel):
def __init__(self, *args, **kwargs):
wx.Panel.__init__(self, *args, **kwargs)
sizer = wx.BoxSizer(wx.VERTICAL)
for x in range(2):
sizer.Add(SampleCollapsiblePane(self, label = str(x)), 0)
self.SetSizer(sizer)
class Main_Frame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.main_panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(wx.Panel(self.main_panel, style=wx.RAISED_BORDER),1, wx.EXPAND)
sizer.Add(SampleSubPanel2(self.main_panel, style = wx.RAISED_BORDER),2, wx.EXPAND )
sizer.Add(wx.Button(self.main_panel,label= "a button"),0,wx.ALIGN_CENTER)
self.main_panel.SetSizer(sizer)
class SampleApp(wx.App):
def OnInit(self):
frame = Main_Frame(None, title = "Sample App")
frame.Show(True)
frame.Centre()
return True
def main():
app = SampleApp(0)
app.MainLoop()
if __name__ == "__main__":
main()

If I am getting you right, you want the code here - allocating more size in sizer to wx.CollapsiblePane when expanded to work properly. The reason it did not work was you forgot to bind the wx.EVT_COLLAPSIBLEPANE_CHANGED. Here is a code which worked for me -
import wx
class SampleCollapsiblePane(wx.CollapsiblePane):
def __init__(self, *args, **kwargs):
wx.CollapsiblePane.__init__(self,*args,**kwargs)
sizer = wx.BoxSizer(wx.VERTICAL)
for x in range(5):
sizer.Add(wx.Button(self.GetPane(), label = str(x)))
self.GetPane().SetSizer(sizer)
self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.on_change)
def on_change(self, event):
self.GetParent().Layout()
class Main_Frame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.main_panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
for x in range(5):
sizer.Add(SampleCollapsiblePane(self.main_panel, label = str(x)), 0)
self.main_panel.SetSizer(sizer)
class SampleApp(wx.App):
def OnInit(self):
frame = Main_Frame(None, title = "Sample App")
frame.Show(True)
frame.Centre()
return True
def main():
app = SampleApp(0)
app.MainLoop()
if __name__ == "__main__":
main()
EDIT: Looks like it may be a bug in wxPython running on windows. Below is screen shot of the the exact same code that has problems on Windows running on Ubuntu with no problems.

Related

WxPython Cut, Copy, Paste functions

I am making a small application and I have trouble defining efficient "Edit" menu functions.
I have tried this:
from pyautogui import hotkey
.
.
.
def OnCopy ( self, event ):
hotkey ( 'ctrl, 'c' )
However, the above doesn't always work and even breaks sometimes. Is there a better method?
wxPython has its own Clipboard object. Its implementation depends on the use of a wxDataObject because, of course, you can copy and paste many types of data onto the clipboard
import wx
class MainFrame(wx.Frame):
"""Create MainFrame class."""
def __init__(self, *args, **kwargs):
super().__init__(None, *args, **kwargs)
self.size = (400, 1000)
self.panel = MainPanel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.panel)
self.SetSizer(sizer)
self.Center()
self.Show()
def on_copy(self, event):
if wx.TheClipboard.Open():
# Put some text onto the clipboard
text = self.panel.txt_input.GetValue()
data_object = wx.TextDataObject(text)
wx.TheClipboard.SetData(data_object)
# Now retrieve it from the clipboard and print the value
text_data = wx.TextDataObject()
success = wx.TheClipboard.GetData(text_data)
wx.TheClipboard.Close()
if success:
print(f'This data is on the clipboard: {text_data.GetText()}')
class MainPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.parent = parent
self.txt_input = wx.TextCtrl(self)
cmd_copy = wx.Button(self, wx.ID_COPY)
cmd_copy.Bind(wx.EVT_BUTTON, parent.on_copy)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.txt_input)
sizer.Add(cmd_copy)
self.SetSizer(sizer)
if __name__ == '__main__':
wx_app = wx.App()
MainFrame()
wx_app.MainLoop()

How to switch panels in python using wxPython

I'm trying to create an App which allows the user to switch the information show using buttons. The basic idea of the code is that the user sees buttons on the left side of the screen and when the user presses "button 1", the code shows Panel1. I've made 2 buttons so far and the code for 2 panels is written as well but i can't figure out how to update my MainFrame so it show a different panel when one of the buttons is pressed.
Code:
import wx
TabNumber = 1
class ButtonPanel(wx.Panel):
def __init__(self, parent):
global TabNumber
super(ButtonPanel, self).__init__(parent, -1)
self.Tab1Button = wx.Button(self, label="TAB 1")
self.Tab1Button.Bind(wx.EVT_BUTTON, self.SwitchTab(1))
self.Tab2Button = wx.Button(self, label="TAB 2")
self.Tab2Button.Bind(wx.EVT_BUTTON, self.SwitchTab(2))
self.Sizer = wx.BoxSizer(wx.VERTICAL)
self.Sizer.Add(self.Tab1Button, wx.CENTER,0)
self.Sizer.Add(self.Tab2Button, wx.CENTER, 0)
self.SetSizer(self.Sizer)
def SwitchTab(self, tab):
def OnClick(event):
print(f"Switch to tab {tab} started")
TabNumber = tab
print(TabNumber)
return OnClick
class Panel1(wx.Panel):
def __init__(self, parent):
super(Panel1, self).__init__(parent, -1)
self.panel = wx.Panel(self)
self.text = wx.StaticText(self.panel, label="1")
class Panel2(wx.Panel):
def __init__(self, parent):
super(Panel2, self).__init__(parent, -1)
self.panel = wx.Panel(self)
self.text = wx.StaticText(self.panel, label="2")
class MainFrame(wx.Frame):
def __init__(self):
super(MainFrame, self).__init__(None, -1, "Test Application")
self.Panels = {
"Panel1": Panel1(self),
"Panel2": Panel2(self)
}
self.MySizer = wx.BoxSizer(wx.HORIZONTAL)
self.tabpanel = ButtonPanel(self)
self.MySizer.Add(self.tabpanel,wx.CENTER,0)
self.InfoPanel = self.Panels["Panel"+str(TabNumber)]
self.MySizer.Add(self.InfoPanel, wx.CENTER,0)
self.SetSizer(self.MySizer)
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
I was also wondering how I can adjust the ratio for the space that is given to my ButtonPanel and my InfoPanel.
As far as I can see, you are trying to do something that works like a Wizard... On the one hand, you can use wx.adv.Wizard. On the other hand, you can look at this tutorial that does something very similar and adapt it to what you need:
WXPython: How to create a generic wizard
Good luck!

wxPython showing help upon mouse focus

I wonder if there is any easy way to popup help (stickies style) upon focusing the mouse on a wx.Button for a second or two.
thanks
I believe you are looking for wx.ToolTip. An example:
import wx
class MyFrame(wx.Frame):
def __init__(self, title, *args, **kwargs):
super(MyFrame, self).__init__(None, title=title, *args, **kwargs)
panel = wx.Panel(self)
button = wx.Button(panel, label="Button")
tooltip = wx.ToolTip("Try to click this button")
button.SetToolTip(tooltip)
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame("Example")
self.frame.Show()
return True
MyApp(False).MainLoop()
There are some methods you can use for controlling the tip.

wxPython Custom Dialog loading incorrectly

I am trying to create a custom dialog to display information. It is activated on a button press, and that mechanism is working perfectly. However, the dialog itself is broken. Any help is appreciated.
import wx
class ForgotPass(wx.Dialog):
def __init__(self, *args, **kwargs):
super(ForgotPass, self).__init__(*args, **kwargs)
self.InitUI()
def InitUI(self):
self.panel = wx.Panel(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.title = wx.StaticText(self.panel, label='Forgotten Your Password?', style=wx.ALIGN_CENTER)
self.title.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, underline=True)) # Underlined & Bold
self.sizer.Add(self.title)
self.text = wx.StaticText(self.panel, label="Contact 'TheHiguty' via ingame PM to have your password reset!", style=wx.ALIGN_CENTER)
self.sizer.Add(self.text)
self.SetSizer(self.sizer)
self.SetSize((200, 150))
self.SetTitle('Forgotten Your Password')
self.Center()
self.Show(True)
def main():
app = wx.App(False)
ForgotPass(None)
app.MainLoop()
if __name__ == "__main__":
main()
However rather than displaying the text correctly, I get this:
Any help to fix this issue is greatly appreciated!
Add self.sizer.Fit(self.panel) after self.SetSizer(self.sizer) to fix the problem.
Although, if you do so, you'll find that the horizontal width is too small, so you might want to expand that. Complete code with fix:
import wx
class ForgotPass(wx.Dialog):
def __init__(self, *args, **kwargs):
super(ForgotPass, self).__init__(*args, **kwargs)
self.InitUI()
def InitUI(self):
self.panel = wx.Panel(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.title = wx.StaticText(self.panel, label='Forgotten Your Password?', style=wx.ALIGN_CENTER)
self.title.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, underline=True)) # Underlined & Bold
self.sizer.Add(self.title)
self.text = wx.StaticText(self.panel, label="Contact 'TheHiguty' via ingame PM to have your password reset!", style=wx.ALIGN_CENTER)
self.sizer.Add(self.text)
self.SetSizer(self.sizer)
self.sizer.Fit(self.panel)
self.SetSize((400, 150))
self.SetTitle('Forgotten Your Password')
self.Center()
self.Show(True)
def main():
app = wx.App(False)
ForgotPass(None)
app.MainLoop()
if __name__ == "__main__":
main()
Although, there is a way to create dialogs that look cleaner and looks more native to whatever OS the program is run on: use wx.MessageBox
Here's a simple example:
import wx
class MainWindow(wx.Frame):
def __init__(self, parent):
super(MainWindow, self).__init__(parent)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.button = wx.Button(self, wx.ID_ANY, label="Test")
self.sizer.Add(self.button)
self.Bind(wx.EVT_BUTTON, self.password_dialog, self.button)
self.SetSizer(self.sizer)
self.sizer.Fit(self)
self.SetTitle('Test')
self.SetSize((100, 100))
self.Centre()
self.Show(True)
def password_dialog(self, event):
wx.MessageBox(
'Contact `TheHiguty` blah blah blah',
'Forgotten your password?',
wx.OK|wx.ICON_ERROR)
if __name__ == '__main__':
app = wx.App()
MainWindow(None)
app.MainLoop()
I understand that this is an older question, but I would like to note what I use for custom dialog boxes and do not have the "noise / icon" (at least in my opinion). This is working with wxPython 2.9.5.0.
This is the code I use:
from wx.lib.pubsub import pub
def showConfirmDlg(message, caption, flag=wx.OK|wx.ICON_NONE):
msg = wx.MessageDialog(None, message=message,
caption=caption, style=flag)
msg.ShowModal()
msg.Destroy()
pub.subscribe(showConfirmDlg, 'dialog.confirm')
As you can see in the snippet I can use this function repeatedly for various messages and eliminate the need for multiple function definitions. To show the dialog box with the text in your example I would do this:
def onButtonPress(self, event):
pub.sendMessage('dialog.confirm',
message="Forgot your password?\n Contact 'TheHiguty' (rest of text)",
caption="Forgot Password?") # Could even just put caption=""
For further re-usability options you can replace the "flag=wx.OK|wx.ICON_NONE" with just "flag" and add "flag=wx.OK|(whatever style)" to your function call.
For more examples regarding Python and wxPython I highly recommend Mike Driscoll's blog site The Mouse vs The Python.I have found it amazingly informative and have learned a lot about wxPython from Mr Driscoll.
Hope this helps you and others!
--EDIT--
I should mention that I found wx.ICON_NONE by accident while trying to remove icons from my message boxes myself.

Learning wxPython, basic thing

I want to display a button that when I click adds to the main panel a static text that automatic adds to the BoxSizer of the panel. I have this code but dosen't work good. Anyone can help me? I am desperate. Thanks
import wx
class MyApp(wx.App):
def OnInit(self):
self.frame = MainFrame(None,title='')
self.SetTopWindow(self.frame)
self.frame.Show()
return True
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(*args, **kwargs)
#Atributos
self.panel = MainPanel(self)
self.CreateStatusBar()
#Layout
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.sizer.Add(self.panel,1,wx.EXPAND)
self.SetSizer(self.sizer)
class MainPanel(wx.Panel):
def __init__(self, parent):
super(MainPanel, self).__init__(parent)
#Atributos
bmp = wx.Bitmap('./img.png',wx.BITMAP_TYPE_PNG)
self.boton = wx.BitmapButton(self,bitmap=bmp)
# Layout
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.sizer.Add(self.boton)
self.SetSizer(self.sizer)
self.Bind(wx.EVT_BUTTON,self.add,self.boton)
def add(self,event):
self.sizer.Add(wx.StaticText(self,label='Testing'))
if __name__ == "__main__":
app = MyApp(False)
app.MainLoop()
If your problem is that your text initially shows up behind the button when it is clicked, you can force the sizer to update by adding a call to your Panel's Layout method.
class MainPanel(wx.Panel):
def __init__(self, parent):
super(MainPanel, self).__init__(parent)
#Atributos
bmp = wx.Bitmap('./img.png',wx.BITMAP_TYPE_PNG)
self.boton = wx.BitmapButton(self,bitmap=bmp)
# Layout
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.sizer.Add(self.boton)
self.SetSizer(self.sizer)
self.Bind(wx.EVT_BUTTON,self.add,self.boton)
def add(self,event):
self.sizer.Add(wx.StaticText(self,label='Testing'))
self.Layout()

Categories

Resources