I am using wxpython...How can I detect and perform a function if someone clicks the Red 'X' on the top right corner ( Close button )? What is the code? Can someone please help me? Thanks!
You are looking for EVT_CLOSE.
e.g.
import wx
class Test(wx.Frame):
def __init__(self,parent):
wx.Frame.__init__(self,parent,title="Main Window",size = (300,200))
panel = wx.Panel(self)
menubar=wx.MenuBar()
firstm=wx.Menu()
fm1 = wx.MenuItem(firstm, -1, 'Quit\tAlt+Q')
firstm.Append(fm1)
self.Bind(wx.EVT_MENU, self.OnExit, id=fm1.GetId())
# Catch Clicking on the Corner X to close
self.Bind(wx.EVT_CLOSE, self.OnExit)
menubar.Append(firstm,"File")
self.SetMenuBar(menubar)
t = wx.StaticText(panel,-1,"Testing 1 2 3 ....", pos=(10,20))
def OnExit(self, event):
# To discover how you got here,
# you can either test the event type or define a separate function for EVT_CLOSE,
# most of the time you don't care
if event.GetEventType() == wx.EVT_CLOSE.typeId:
print("Close using X")
else:
print("Close using Menu or Alt+Q")
self.Destroy()
if __name__=='__main__':
app=wx.App()
frame=Test(None)
frame.Show()
app.MainLoop()
Related
Issue:
I'm experiencing an issue where a function that simply creates a Grid() works when called in one place, but not another place. When it is called from the "other," non-working place, it does create a very small square in the corner of the window. At this time, I don't understand why, and I'm hoping someone can help.
Code: (feel free to copy paste this into a text editor and give it a go!)
import wx
import wx.grid as gridlib
class MainFrame(wx.Frame):
def __init__(self, parent, title):
super(MainFrame, self).__init__(parent, title=title,
size=(350, 250))
self.init_ux()
self.main_grid = None
def init_ux(self):
menu_bar = wx.MenuBar()
file_menu = wx.Menu()
file_menu.AppendSeparator()
menu_bar.Append(file_menu, '&File')
# add open file menu
open_menu = wx.Menu()
my_btn = open_menu.Append(wx.ID_ANY, 'button description')
# append open_menu to the file_menu
file_menu.Append(wx.ID_OPEN, '&Open', open_menu)
self.SetMenuBar(menu_bar)
self.Bind(wx.EVT_MENU, lambda event: self.open_dialog(data="i love string literals."), my_btn)
self.SetSize((300, 200))
self.Centre()
# the create_grid_view() below DOES work when uncommented
#self.create_grid_view(10, 10)
def create_grid_view(self, row_count, col_count):
print("Creating grid view!")
# set up grid panel
panel = wx.Panel(self)
self.main_grid = gridlib.Grid(panel)
self.main_grid.CreateGrid(row_count, col_count)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.main_grid, 1, wx.EXPAND)
panel.SetSizer(sizer)
def open_dialog(self, data):
# data is being used for populating wildcard, etc
with wx.FileDialog(self, "Open a file!",
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return
file_path = fileDialog.GetPath()
try:
# here, I do some fun data "things" with the file_path
# open it, use other functions, etc.
# afterwards, I need to create a grid
self.create_grid_view(10, 10)
# ^^ This creates a small square instead of a grid.
except IOError:
wx.LogError("Cannot open file '%s'." % file_path)
if __name__ == "__main__":
app = wx.App()
frame = MainFrame(None, title='Window Title :)')
frame.Show()
app.MainLoop()
Expectation:
Actual Results:
Summary:
Why does the create_grid_view() function display a proper grid when called from the init_ux() function, but not the open_dialog() function?
Thanks in advance!
Where you have panel.SetSizer(sizer) either use:
panel.SetSizerAndFit(sizer)
or use:
panel.SetSizer(sizer)
panel.Fit()
I am trying to create simple program which will click to specific coordinates every x seconds based on your choose if you need to click on Left screen or Right screen. My issue here is that win32api.SetCursorPos which is moving with my cursor is not moving to the secondary screen (right in my case). It stays in the main screen.
And I am having one more issue with my code, when Exit button inside GUI is pressed, window will close however program is still running in background. I am using self.Destroy() function to kill all process.
Thank you for your advice.
Here is my code:
import time
import pyautogui
import wx
import threading
import sys
import win32api
class bucky(wx.Frame):
def __init__(self,parent,id):
self.positionx = ""
self.positiony = ""
wx.Frame.__init__(self,parent,id,'AutoClick 2.0', size=(300,200))
panel=wx.Panel(self)
self.buttonpos=wx.Button(panel,label="Left Screen",pos=(30,10),size=(80,40))
self.buttonpos2=wx.Button(panel,label="Right Screen",pos=(180,10),size=(80,40))
self.button=wx.Button(panel,label="Start",pos=(120,90),size=(60,30))
self.button2=wx.Button(panel,wx.ID_EXIT,label="Exit",pos=(120,120),size=(60,30))
self.Bind(wx.EVT_BUTTON, self.action, self.button)
self.Bind(wx.EVT_BUTTON, self.closebutton, self.button2)
self.Bind(wx.EVT_BUTTON, self.position, self.buttonpos)
self.Bind(wx.EVT_BUTTON, self.position, self.buttonpos2)
self.Bind(wx.EVT_CLOSE, self.closewindow)
def position(self, event):
label = event.GetEventObject().GetLabel()
if label == "Left Screen":
self.positionx = 1640
self.positiony = 183
self.buttonpos.Disable()
self.buttonpos2.Enable()
elif label == "Right Screen":
self.positionx = 3308
self.positiony= 186
self.buttonpos.Enable()
self.buttonpos2.Disable()
def closebutton(self,event):
self.Destroy()
def closewindow(self,event):
self.Destroy()
def action(self,event):
self.button.Disable()
def callback():
while 1:
pos = pyautogui.position()
time.sleep(10)
pos1 = pyautogui.position()
if (pos1[0] == pos[0]) and (pos1[1] == pos[1]):
win32api.SetCursorPos((self.positionx, self.positiony))
pyautogui.click()
else:
pass
t = threading.Thread(target=callback)
t.start()
if __name__=='__main__':
app=wx.PySimpleApp()
frame=bucky(parent=None,id=1)
frame.Show()
app.MainLoop()
EDIT: Problem have been solved. Thank you for help.
Looking at this reddit post, I think the x coordinates for the secondary monitor are simply added to the resolution of the primary monitor (ex if you had two 1920x1080 monitors, the middle of the second monitor will be at 2880,520)
Try using (win32api.GetSystemMetrics(MONITOR_NUMBER) to see how the differences are represented.
How can i get a value from button click from my frame?
btnYes = wx.Button(panel, -1, "OK")
self.Bind(wx.EVT_BUTTON, self.clickYes, btnYes)
def clickYes(self, evt):
print "clicked Yes"
self.Close()
whenever a user click yes , i want to get a value to check in other module. Something like confirmation flag. When user is confirmed one item then carrying on doing other items. The confirmation flag i will be using is here below :
def my_methodABC():
matchList = []
for x, y in product(d1rows, d2rows):
if userConfirmedFromWxPythonClickYesButton():
matchList.append(abc)
return matchList
Use a MessageDialog. There are lots of examples on the web. Here are a couple:
http://wiki.wxpython.org/MessageBoxes
http://www.blog.pythonlibrary.org/2010/06/26/the-dialogs-of-wxpython-part-1-of-2/
And here's a really simple example:
import wx
########################################################################
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Test")
panel = wx.Panel(self)
btn = wx.Button(panel, label="Ask Question")
btn.Bind(wx.EVT_BUTTON, self.showMessageDlg)
#----------------------------------------------------------------------
def showMessageDlg(self, event):
"""
Show a message
"""
msg = "Do you want to continue?"
title = "Question!"
style = wx.YES_NO|wx.YES_DEFAULT|wx.ICON_QUESTION
dlg = wx.MessageDialog(parent=None, message=msg,
caption=title, style=style)
result = dlg.ShowModal()
if result == wx.ID_YES:
print "User pressed yes!"
else:
print "User pressed no!"
dlg.Destroy()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
You would probably want to call your match list method if the user pressed the yes button rather than just printing a message to stdout though.
I am building a simple signal generator in Python based on the Pyo and WX libraries.
I have ran through the simple tutorials for each and have successfully bound buttons in WX to WX functions. I am now trying to generate a simple sine wave(at 440 hz) for 1 second by pressing the button labeled "Oscillator 1"; however, when the main() function executes, the sine tone is played and while the button is displayed in the wx frame I am unable to retrigger the sine tone. Both of these symptoms are unwanted.
Why does the sine tone play immediately on program execution? Why does the firstOSC button seemingly not work?
import wx
from pyo import *
import time
pyoServer = Server().boot()
pyoServer.start()
class MainWindow(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self,parent,title=title, size = (640,640))
self.CreateStatusBar() # A StatusBar in the bottom of the window
# Signal Generator controls
oscillator = SoundOutput()
firstOSC = wx.Button(self, wx.ID_YES,"Oscillator 1 " + str(oscillator.str_osc1State))
self.Bind(wx.EVT_BUTTON, oscillator.OnOff1(440), firstOSC)
#Menus
filemenu = wx.Menu()
menuExit = filemenu.Append(wx.ID_EXIT,"&Exit","Terminate the program")
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File")
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
self.Show(True)
def OnExit(self,e):
self.Close(True)
class SoundOutput(object):
def __init__(self):
self.osc1State = False
self.str_osc1State = "Off"
self.a = Sine(440, 0, 0.1)
def OnOff1(self, frequency):
self.a.freq = frequency
self.a.out()
time.sleep(1)
self.osc1State = True
def Main():
app = wx.App(False)
frame = MainWindow(None,"Signal Generator")
app.MainLoop()
I solved this by investigating how WX handles events. As it turns out, for some reason calling a method in a nested or separate instance of a class caused the tone to play at runtime instead of on the event. I fixed this by making a method for the MainWindow class that serves as the binded event handler for firstOSC. This method then calls the requisite methods for the actual oscillator class.
Here is the new code:
# Signal Generator controls
self.fOscillator = SoundOutput()
self.fOscillatorstatus = False
self.firstOSC = wx.Button(self, wx.ID_ANY,"Oscillator 1 On")
self.firstOSC.Bind(wx.EVT_BUTTON, self.OnFirstOSC)
def OnFirstOSC(self,e):
if not self.fOscillatorstatus:
self.fOscillator.OnOff1(440)
self.fOscillatorstatus = True
self.firstOSC.SetLabel("Oscillator 1 Off")
elif self.fOscillatorstatus:
self.fOscillator.OnOff1(0)
self.firstOSC.SetLabel("Oscillator 1 On")
self.fOscillatorstatus = False
I have a wx.Frame, in which there is a main wx.Panel with several widgets inside of it. I want one button in there to cause a "help panel" to come up. This help panel would probably be a wx.Panel, and I want it to overlay the entire main wx.Panel (not including the menu bar of the wx.Frame). There should be some sort of close button on the help button that will make it disappear again.
What is a good way to achieve this? I've looked into wx.Notebook but haven't found a way to make it not show the tabs.
Note that I don't want to destroy and recreate the help panel every time the user closes and opens it: I just want it to be hidden.
There are several ways
a) you can create a custom child panel, and make it same size and position at 0,0 among top of all child widgets. no need of destroying it just Show/Hide it
this also resizes with parent frame
b) popup a wx.PopupWindow or derived class and place and size it at correct location
so as suggest in a) here is an example, where all controls are put in panel using sizer, as separate help cntrl is created which can be shown/hidden from button, but you can create a custom cntrl which hides itself on clicking close
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self)
# create controls
self.cntrlPanel = wx.Panel(self.panel)
stc1 = wx.StaticText(self.cntrlPanel, label="wow it works")
stc2 = wx.StaticText(self.cntrlPanel, label="yes it works")
btn = wx.Button(self.cntrlPanel, label="help?")
btn.Bind(wx.EVT_BUTTON, self._onShowHelp)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(stc1)
sizer.Add(stc2)
sizer.Add(btn)
self.cntrlPanel.SetSizer(sizer)
# create help panel
self.helpPanel = wx.Panel(self.panel)
self.stcHelp = wx.StaticText(self.helpPanel, label="help help help\n"*8)
btn = wx.Button(self.helpPanel, label="close[x]")
btn.Bind(wx.EVT_BUTTON, self._onShowCntrls)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.stcHelp)
sizer.Add(btn)
self.helpPanel.SetSizer(sizer)
self.helpPanel.Hide()
self.helpPanel.Raise()
self.helpPanel.SetBackgroundColour((240,250,240))
self.Bind(wx.EVT_SIZE, self._onSize)
self._onShowCntrls(None)
def _onShowHelp(self, event):
self.helpPanel.SetPosition((0,0))
self.helpPanel.Show()
self.cntrlPanel.Hide()
def _onShowCntrls(self, event):
self.cntrlPanel.SetPosition((0,0))
self.helpPanel.Hide()
self.cntrlPanel.Show()
def _onSize(self, event):
event.Skip()
self.helpPanel.SetSize(self.GetClientSizeTuple())
self.cntrlPanel.SetSize(self.GetClientSizeTuple())
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()
Updated code for Python 3.9.4 and wxPython 4.1.1. Hope people find it useful. The original post was useful to me, but as a neophyte, it took me some effort to have it run properly with recently released tools.
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self)
# create controls
self.cntrlPanel = wx.Panel(self.panel)
stc1 = wx.StaticText(self.cntrlPanel, label="wow it works")
stc2 = wx.StaticText(self.cntrlPanel, label="yes it works")
btn = wx.Button(self.cntrlPanel, label="help?")
btn.Bind(wx.EVT_BUTTON, self._onShowHelp)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(stc1)
sizer.Add(stc2)
sizer.Add(btn)
self.cntrlPanel.SetSizer(sizer)
# create help panel
self.helpPanel = wx.Panel(self.panel)
self.stcHelp = wx.StaticText(self.helpPanel, label="help help help\n"*8)
btn = wx.Button(self.helpPanel, label="close[x]")
btn.Bind(wx.EVT_BUTTON, self._onShowCntrls)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.stcHelp)
sizer.Add(btn)
self.helpPanel.SetSizer(sizer)
self.helpPanel.Hide()
self.helpPanel.Raise()
self.helpPanel.SetBackgroundColour((240,250,240))
self.Bind(wx.EVT_SIZE, self._onSize)
self._onShowCntrls(None)
def _onShowHelp(self, event):
self.helpPanel.SetPosition((0,0))
self.helpPanel.Show()
self.cntrlPanel.Hide()
def _onShowCntrls(self, event):
self.cntrlPanel.SetPosition((0,0))
self.helpPanel.Hide()
self.cntrlPanel.Show()
def _onSize(self, event):
event.Skip()
self.helpPanel.SetSize(self.GetClientSize())
self.cntrlPanel.SetSize(self.GetClientSize())
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()