showing another window/frame in wxPython - python

I am fairly new to programming and to python and wxpython. I have looked over this code for literally HOURS and I tried finding an answer everywhere online. I am having trouble getting a new window to show up after a menu item is clicked. Here is my code so far...
import wx
class MainWindow(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Python Test App',size=(600,400))
panel=wx.Panel(self)
wx.Frame.CenterOnScreen(self)
##wx.Frame.Maximize(self)
status=self.CreateStatusBar()
menubar=wx.MenuBar()
file_menu=wx.Menu()
edit_menu=wx.Menu()
ID_FILE_NEW = 1
ID_FILE_OPEN = 2
ID_EDIT_UNDO = 3
ID_EDIT_REDO = 4
file_menu.Append(ID_FILE_NEW,"New Window","This is a new window")
file_menu.Append(ID_FILE_OPEN,"Open...","This will open a new window")
edit_menu.Append(ID_EDIT_UNDO,"Undo","This will undo your last action")
edit_menu.Append(ID_EDIT_REDO,"Redo","This will redo your last undo")
menubar.Append(file_menu,"File")
menubar.Append(edit_menu,"Edit")
self.SetMenuBar(menubar)
self.Bind(wx.EVT_MENU, NewWindow.new_frame, None, 1)
class NewWindow(wx.Frame):
def __init__(self,MainWindow,id):
wx.Frame.__init__(self, None, id, 'New Window', size=(600,400))
wx.Frame.CenterOnScreen(self)
self.Show(False)
def new_frame(self, event):
NewWindow.Show(True)
if __name__=='__main__':
app=wx.PySimpleApp()
frame=MainWindow(parent=None,id=-1)
frame.Show()
app.MainLoop()
When I try to run this code, I get this error message once I click on the menu item "New Window"
TypeError: unbound method new_frame() must be called with NewWindow instance as first argument (got CommandEvent instance instead)
Again, I am fairly new to programming. Any help is greatly appreciated and also, I know my code may not be the "cleanest" looking code around. Thanks in advance!

You don't seem to understand how classes work in Python. You try to call NewWindow.new_frame, but you never actually create an instance of that class.
The error message is because you are calling the method on the class instead of on an instance of the class. What you want to do is something like:
newWin = NewWindow(...) # replace ... with the appropriate parameters
newWin.Show(True)
You don't provide enough information in your example to know what the appropriate parameters are for the NewWindow call (e.g., you don't show where you create the main window), but the MainWindow and id parameters in NewWindow.__init__ aren't just there for looks: wxPython needs to know the parent window. You should look into the wxPython documentation to understand how to create a wxFrame.

Modifying your code to some extent i was able to show a new window when user clicks a New Window option,
Do check the stuff that i have modified a let me know if this is what you want??
import wx
class MainWindow(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Python Test App',size=(600,400))
panel=wx.Panel(self)
wx.Frame.CenterOnScreen(self)
status=self.CreateStatusBar()
menubar=wx.MenuBar()
file_menu=wx.Menu()
edit_menu=wx.Menu()
ID_FILE_NEW = 1
ID_FILE_OPEN = 2
ID_EDIT_UNDO = 3
ID_EDIT_REDO = 4
file_menu.Append(ID_FILE_NEW,"New Window","This is a new window")
file_menu.Append(ID_FILE_OPEN,"Open...","This will open a new window")
edit_menu.Append(ID_EDIT_UNDO,"Undo","This will undo your last action")
edit_menu.Append(ID_EDIT_REDO,"Redo","This will redo your last undo")
menubar.Append(file_menu,"File")
menubar.Append(edit_menu,"Edit")
self.SetMenuBar(menubar)
self.Bind(wx.EVT_MENU, self.test, None, 1)
def test(self, event):
self.new = NewWindow(parent=None, id=-1)
self.new.Show()
class NewWindow(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self, parent, id, 'New Window', size=(400,300))
wx.Frame.CenterOnScreen(self)
#self.new.Show(False)
if __name__=='__main__':
app=wx.PySimpleApp()
frame=MainWindow(parent=None,id=-1)
frame.Show()
app.MainLoop()

Related

how to Bind the EVT_TASKBAR_LEFT_DOWN in wxPyton for TaskBarIcon class

I am new to Python and using wxPython to build a tool which can be minimize to system tray icon and can be restored when left clicked.
I have following code in Python 3. While searching online I found that I can bind the key like following snippet:
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.OnTaskBarLeftClick)
However this is causing error saying that wx doesn't have this attribute.
I want to restore the main window when the task bar icon is left clicked, while right click creates a pop-up menu.
import wx
import wx.adv
class SysTrayMenu(wx.adv.TaskBarIcon):
def __init__(self, frame, icon=None, menu=None):
wx.adv.TaskBarIcon.__init__(self)
self.frame = frame
self.menu = menu
self.icon = icon
app_icon = wx.Icon()
app_icon.CopyFromBitmap(wx.Bitmap(self.icon, wx.BITMAP_TYPE_PNG))
self.SetIcon(app_icon)
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.OnTaskBarLeftClick)
def OnTaskBarLeftClick(self, evt):
self.frame.Show()
self.frame.Recover()
def CreatePopupMenu(self):
new_menu = wx.Menu()
return self.menu
Since the TaskBarIcon class is in the wx.adv module then the coresponding event binders and event type IDs are also in wx.adv.
I was doing it wrong. To make the menu pop-up I had to bind it with wx.EVT_MENU.
self.Bind(wx.EVT_MENU, self.click_me, id=self.m_cb.GetId())
Use the code below instead. Pls. note the event should be wx.adv.EVT_TASKBAR_LEFT_DOWN, not wx.adv.wxEVT_TASKBAR_BALLOON_CLICK. I was mislead by the prompts of pycharm before.
self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)

How do I bind wx.CLOSE_BOX to a function?

Fairly simple question, but I can't seem to find the answer. I have a GUI which has a cancel button that asks the user to abort all unsaved changes when they press it. The GUI also has a wx.CLOSE_BOX, but this simply closes it because its not bound to my OnCancel function. How do I bind it?
Things I tried:
self.Bind(wx.EVT_CLOSE, lambda event: self.OnCancel(event, newCRNum), wx.CLOSE_BOX)
#This gives an AssertionError, replacing wx.EVT_CLOSE with wx.EVT_BUTTON also
# gives the same error
self.Bind(wx.EVT_CLOSE, lambda event: self.OnCancel(event, newCRNum))
#This binds any time ```self.Close(True)``` occurs (which makes sense) but
# is not what I want. There are other buttons which close the GUI which should not
# use the OnCancel function
Thanks in advance for your help
EDIT: The code below should help clarify what I'm looking for
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
newCRNum = 0
cancelBtn = wx.Button(self, -1, "Cancel")
self.Bind(wx.EVT_BUTTON, lambda event: self.OnCancel(event, newCRNum), cancelBtn)
def OnCancel(self, event, CRNum):
dlg = wx.MessageDialog(self, "Are you sure you want to cancel? All work will be lost and CR number will not be reserved.", "Cancel CR", wx.YES_NO|wx.NO_DEFAULT|wx.ICON_EXCLAMATION)
if dlg.ShowModal() == wx.ID_YES:
self.Destroy()
else:
dlg.Destroy
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show()
app.MainLoop()
So what this does is create a whimsically large cancel button. When this button is pressed, a dialog box pops up and prompts the user if they really want to quit. If they say yes, the whole gui closes, if not, only the dialog box closes.
When the user presses the red (X) button in the top right of the GUI, I want the same thing to happen. Since is a button, I assume it can be bound to my OnCancel button, but how do I do this?
Reason for AssertionError: The third argument must be source widget like follow.
self.Bind(wx.EVT_CLOSE, lambda event: self.OnCancel(event, newCRNum), self)
Try following example:
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
newCRNum = 0
self.Bind(wx.EVT_CLOSE, lambda event: self.OnCancel(event, newCRNum))
def OnCancel(self, event, num):
dlg = wx.MessageDialog(self, 'Do you want close?', 'Sure?',
wx.OK|wx.CANCEL|wx.ICON_QUESTION)
result = dlg.ShowModal()
if result == wx.ID_OK:
event.Skip()
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show()
app.MainLoop()

wxPython: call a wxApp from another wxApp

Is it possible to run a wxApp from another wxApp?
I am trying to simply call a program I wrote (called DataDeck) from a method of another wxApp, like it was a plugin.
something like:
def on_datadeck_btn_click(self, event):
import datadeck.main
datadeck.main.run()
event.Skip()
where datadeck.main.run() is a classic start of a wxApp:
def run():
app = DataDeck(0)
app.SetAppName("DataDeck")
app.MainLoop()
Right now, it correctly opens DataDeck the first time and it works, but it won't reopen DataDeck a second time after I close it. This would freeze everything.
Update: based on #Mike Driscoll answer, I documented myself more and came to the following solution:
I added an "entry point" in datadeck
def run_as_plugin():
#[do some stuff related to XRC layouts and sysout redirection]
MainGUI = datadeck.gui.maingui.MainGUI()
Where the constructor of MainGUI() automatically shows the wxFrame. Now my application behaves like it was a component of the caller wxApp.
Therefore, I modify the application method as follows:
def on_datadeck_btn_click(self, event):
import datadeck.main
datadeck.main.run_as_plugin()
event.Skip()
It was very simple, indeed! I just had to modify my objects that deal with stdout redirection (not part of this question, I omit the details), and everything worked fine.
There should only be on wx.App. From what I've read online, you can't have two wx.App objects running in one script. You could probably do it using the subprocess module to open a new process though. Take a look at Editra to see some examples for how to do plugins. It is included with wxPython or you can download it separately.
It is perfectly feasible. Not sure why it doesnt work for you.
This example works perfectly:
--main.py--
import wx
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title='Main', size=(353,270))
button= wx.Button(self, -1, 'call app', pos=(10,10), size=(-1,30))
self.Bind(wx.EVT_BUTTON, self.capp, button)
def capp(self, event):
import datadeck
datadeck.run()
if __name__ == '__main__':
app = wx.App(0)
frame = MainFrame(None)
frame.Show()
app.MainLoop()
--datadeck.py--
import wx
class DDFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title='DDFrame', size=(353,270))
button = wx.Button(self, -1, 'print something', pos=(100,100), size=(-1,30))
self.Bind(wx.EVT_BUTTON, self.say_hello, button)
def say_hello(self, event):
print 'something'
class DataDeck(wx.App):
def OnInit(self):
frame = DDFrame(None)
frame.Show()
return True
def run():
app = DataDeck(1)
app.SetAppName("DataDeck")
app.MainLoop()
if you press the 'call app' button you get the new frame open. And you can open as many as you want.
Created aplications/frames are independent of each other. You can close any of them without affecting the others. And the system doesnt freeze.

Adding a widget with a button - wxPython

I'm trying to create something like the categories panel in Wordpress, with wxPython.
What I'm trying to figure out, is how to add a widget when the user clicks a button (like "Add New Category")
Here is my code:
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(300,200))
self.panel = wx.Panel(self, -1)
button = wx.Button(self.panel,-1,"Button")
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(button)
add_btn = wx.Button(self.panel,-1,"Add")
add_btn.Bind(wx.EVT_BUTTON, self.add)
hbox = wx.BoxSizer(wx.HORIZONTAL)
hbox.Add(add_btn)
main_vbox = wx.BoxSizer(wx.VERTICAL)
main_vbox.Add(self.vbox)
main_vbox.Add(hbox)
self.panel.SetSizer(main_vbox)
self.Centre()
self.Show(True)
def add(self,event):
self.vbox.Add((wx.Button(self.panel,-1,"Button")))
if __name__ == "__main__":
app = wx.App()
MainWindow(None, -1, 'Add a Button')
app.MainLoop()
My problem is, the button gets added on top of the previous button. I'm rather mystified by this, because if I delete the event argument of the add() function, and then call it in the __init__ method, self.add(), it works fine. But that doesn't help me any because I need to add the widgets when the user clicks the button.
Any help is much appreciated.
Call self.panel.Layout() after adding the button. This function is called automatically when you resize a window with children (try it with your current code), but not when you add widgets to it.

create a gtk.window under a gtk.widget

I wanna show a gtk.Window under a gtk.widget.
But I don't know how to retrieve the gtk.widget's coordinates for my gtk.window.
Anyone knows ?
Thanks.
You can use the "window" attribute of the gtk.Widget to get the gtk.gdk.Window associated with it. Then look at the get_origin() method to get the screen coordinates.
These coordinates are for the top-level window, I believe (I could be wrong about that, but my code below seems to support that). You can use the get_allocation() method to get the coordinates of a widget relative to its parent.
I got the idea from here. Be warned though: some window managers ignore any initial settings for window position. You might want to look at this post for more info, but I haven't personally checked it out.
Were you intending to create another top-level window? Or a popup window?
#!/usr/bin/env python
import sys
import pygtk
import gtk
class Base:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("destroy", self.on_destroy)
self.box = gtk.VButtonBox()
self.buttons = [
gtk.Button("Test 1"),
gtk.Button("Test 2"),
gtk.Button("Test 3")
]
for button in self.buttons:
self.box.add(button)
button.connect("clicked", self.show_coords)
button.show()
self.window.add(self.box)
self.box.show()
self.window.show()
def show_coords(self, widget, data=None):
print "Window coords:"
print self.window.get_window().get_origin()
print "Button coords:"
print widget.get_allocation()
def on_destroy(self, widget, data=None):
gtk.main_quit()
def main(self):
gtk.main()
if __name__ == "__main__":
base = Base()
base.main()

Categories