How to use wxPython to create a dialogue box? - python

I am learning to use wxPython to build a dialogue based program.
I tried following code (simply copied from wxPython Demo):
import wx
#---------------------------------------------------------------------------
class TestPanel(wx.Panel):
def __init__(self, parent, log):
self.log = log
wx.Panel.__init__(self, parent, -1)
b = wx.Button(self, -1, "Create and Show a DirDialog", (50,50))
self.Bind(wx.EVT_BUTTON, self.OnButton, b)
def OnButton(self, evt):
# In this case we include a "New directory" button.
dlg = wx.DirDialog(self, "Choose a directory:",
style=wx.DD_DEFAULT_STYLE
#| wx.DD_DIR_MUST_EXIST
#| wx.DD_CHANGE_DIR
)
# If the user selects OK, then we process the dialog's data.
# This is done by getting the path data from the dialog - BEFORE
# we destroy it.
if dlg.ShowModal() == wx.ID_OK:
self.log.WriteText('You selected: %s\n' % dlg.GetPath())
# Only destroy a dialog after you're done with it.
dlg.Destroy()
#---------------------------------------------------------------------------
def runTest(frame, nb, log):
win = TestPanel(nb, log)
return win
#---------------------------------------------------------------------------
overview = """\
This class represents the directory chooser dialog. It is used when all you
need from the user is the name of a directory. Data is retrieved via utility
methods; see the <code>DirDialog</code> documentation for specifics.
"""
if __name__ == '__main__':
import sys,os
import run
run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
I ran above code both in Python IDLE and Apatana Studio 3. Here is what I got.
In Python IDLE, I've got:
IDLE Subprocess: no IP port passed in sys.argv.
And in Apatana Studio 3, I've got:
Traceback (most recent call last):
File "C:\Users\User\My Documents\Aptana Studio 3 Workspace\Test Dialogue\main.py", line 61, in
import run ImportError: No module named run
May I know what am I wrong? Thanks a lot. :)

The ImportError is the Python interpreter (the program that runs your Python code) letting you know it cannot find the module (.py file) you are trying to import. Specifically, the error is saying it cannot find the module "run" which you have asked it to import on line 61.
When you do an import in Python, the interpreter searches a bunch of places for the module. One of those is the current directory, and the rest are standard places like where Python libraries are installed. This page has some info about it: http://docs.python.org/2/tutorial/modules.html#the-module-search-path. You'll actually get the same ImportError if you run the program from the command line. It's a Python error, not an Apatana Studio 3 error.
So if you copy "run.py" into the directory with your Python file, the Python interpreter will be able to easily find it when you asks it to import. Another way is to leave the run.py module where it is and change the sys.path at runtime, or add the module location to the PYTHONPATH variable (see link above for more info).
The run.py module isn't needed for what you are trying to achieve though. Here's an example of your code without the run.py module being imported. I'll warn that I'm new to wxPython myself so there may be better ways to do it ;-)
import wx
# This Log class is copied from the run module
class Log(object):
def WriteText(self, text):
if text[-1:] == '\n':
text = text[:-1]
wx.LogMessage(text)
write = WriteText
class TestPanel(wx.Panel):
def __init__(self, parent, log):
self.log = Log()
wx.Panel.__init__(self, parent, -1)
b = wx.Button(self, -1, "Create and Show a DirDialog", (50,50))
self.Bind(wx.EVT_BUTTON, self.OnButton, b)
def OnButton(self, evt):
# In this case we include a "New directory" button.
dlg = wx.DirDialog(self, "Choose a directory:",
style=wx.DD_DEFAULT_STYLE
#| wx.DD_DIR_MUST_EXIST
#| wx.DD_CHANGE_DIR
)
# If the user selects OK, then we process the dialog's data.
# This is done by getting the path data from the dialog - BEFORE
# we destroy it.
if dlg.ShowModal() == wx.ID_OK:
self.log.WriteText('You selected: %s\n' % dlg.GetPath())
# Only destroy a dialog after you're done with it.
dlg.Destroy()
class Frame ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__(self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size(300, 150))
panel = TestPanel(self, -1)
class App(wx.App):
def OnInit(self):
self.frame = Frame(parent=None)
self.frame.Show()
self.SetTopWindow(self.frame)
return True
if __name__ == '__main__':
app = App()
app.MainLoop()
I'm not sure what's happening with the error in IDLE though. That's weird!

Related

IpAddrCtrl is broken on GTK

I have an IpAddrCtrl element from wx.lib.masked in my simple test:
import wx
import wx.lib.masked as masked
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,500))
self.ipaddr1 = masked.IpAddrCtrl(self, -1, style=wx.TE_PROCESS_TAB)
self.grid_ip = wx.FlexGridSizer(cols=2, vgap=10, hgap=10)
self.grid_ip.Add(self.ipaddr1, 0, wx.ALIGN_LEFT)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.grid_ip, 0, wx.ALIGN_LEFT | wx.ALL, border=5)
self.SetSizer(self.sizer)
self.Bind(wx.EVT_TEXT, self.OnIpAddrChange, id=self.ipaddr1.GetId())
self.Show(True)
def OnIpAddrChange(self, event):
print(self.ipaddr1.GetAddress())
print(self.ipaddr1.IsValid())
if __name__ == '__main__':
app = wx.App(False)
frame = MainWindow(None, "IpAddrCtrl sample")
app.MainLoop()
After application starts, I try to insert correct numeric values in my IpAddrCtrl element, but it falls with following log:
<BaseMaskedTextCtrl: . . . >
False
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 3016, in _OnChar
keep_processing = self._keyhandlers[key](event)
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 3937, in _OnChangeField
self._AdjustField( pos )
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 4291, in _AdjustField
newfield = field._AdjustField(slice)
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 1649, in _AdjustField
intStr = str(long(intStr))
ValueError: invalid literal for long() with base 10: '.'
Here a screencast how does it actually works.
Similar demo from wxPython project causes same problems for me.
My environment: Debian Stretch with Python 2.7.13, wxPython 3.0.2 and GTK2 2.24.31-2 from official stable repositories.
Any advices?
Thanks for the help!
Update:
I try to use same sample on Win32 platform and there are no any problems with IpAddrCtrl. After some debugging I found out that the difference in GTK/Win versions in additional EVT_TEXT event call that occurs in IpAddrCtrl when I try to edit selected text region. That causes unnecessary _OnTextChange event handler call, that brokes input.
That strange behavior described on wxPython wiki and commented in a sources of base class for IpAddrCtrl, but currently a have no idea how to fix it.
It's a horrible control, which as far as I can tell, simply doesn't function intuitively at all.
You have to press back space,to clear the field, as you enter each segment of the ip address or it gets itself in a complete tizzy.
Replace your lines
ipaddr = self.FindWindowById(event.GetId())
print ipaddr
print ipaddr.IsValid()
with
print (self.ipaddr1.GetAddress())
print (self.ipaddr1.IsValid())
Clearly, it's a function that needs work, as it is broken in wxpython 4 as well.

wxPython New, Save, and SaveAs Methods

I'm writing a UI for a python app with wxPython. I've handled a few of the OnX functions but I need help with OnNew and OnSave/SaveAs
Here is my Save and SaveAs code:
def OnSave(self, event):
self.dirname = ""
saveFileDialog = wx.FileDialog(self, "Save Operation File", self.dirname, "",
"Operation Files (*.fwr)|*.fwr|All Files (*.*)|*.*", wx.SAVE|wx.OVERWRITE_PROMPT)
if saveFileDialog.ShowModal() == wx.ID_OK:
contents = self.control.GetValue()
self.filename = saveFileDialog.GetFilename()
self.dirname = saveFileDialog.GetDirectory()
filehandle = open(os.path.join(self.dirname, self.filename), 'w')
filehandle.write(contents)
filehandle.close()
else:
sys.exit(1)
saveFileDialog.Destroy()
def OnSaveAs(self, event):
self.dirname = "";
saveAsFileDialog = wx.FileDialog(self, "Save Operation File As", self.dirname, "",
"Operation Files (*.fwr)|*.fwr|All Files (*.*)|*.*",
wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
if saveAsFileDialog.ShowModal() == wx.ID_OK:
contents = self.control.GetValue()
self.filename = saveFileDialog.GetFilename()
self.dirname = saveFileDialog.GetDirectory()
filehandle = open(os.path.join(self.dirname, self.filename), 'w')
filehandle.write(contents)
filehandle.close()
else:
sys.exit(1)
saveFileDialog.Destroy()
# save current contents in the file
# use wxPython output streams
#output_stream = wx.FileOutputStream(saveFileDialog.GetPath())
#if not output_stream.IsOk():
# wx.LogError("Cannot save contents of Operations File '%s'" % saveFileDialog.GetPath())
# return
the commented part at the bottom is another way I found to do it, is using the input and output streams more correct than the way it currently is? Also heres my other question, I got OnNew Working, here is the code:
def OnNew(self, event):
homedir = os.environ['HOME']
if not os.path.exists(homedir):
if getpass.getuser():
homedir = "C:/Users/" + getpass.getuser() + "/"
else:
homedir = "C:/"
newFileDialog = wx.FileDialog(self, "New Operation File", homedir, "",
"Operation Files (*.fwr)|*.fwr|All Files|(*.*)|*.*", wx.FD_CREATE|wx.OVERWRITE_PROMPT)
Everything is great but the OnOpen Method opens an open file dialog, I want a create file dialog (would this be the same as save? Can someone give me an example OnOpen method, and give me some insight into my OnSave and OnSaveAs methods? As you can see there are three aproaches, one in OnSaveAs, one in OnSave, and one commented out at the bottom of OnSaveAs(). As well as many more I didn't write down here. My main question though is how to get the filedialog for new to be a save dialog where you create a file, rather than an open dialog.
Thanks a ton.
SUMMARY:
1) How do you bring up a FileDialog that allows creation of blank files. I assum it will be similar to save, but hwatever ID flags I pass it always gives me an Open button
2) And as for save methods, is it better to do what I showed in the code, or use streams like the commented out section in SaveAs?.
To get the Save dialog, you need to pass the wx.SAVE style flag to your FileDialog object: style=wx.SAVE. You can read more about the save flag here or here.
Here's some example code that worked for me on Xubuntu 14.04 with wxPython 2.8.12.1 and Python 2.7:
import os
import wx
wildcard = "Python source (*.py)|*.py|" \
"All files (*.*)|*.*"
########################################################################
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"File and Folder Dialogs Tutorial")
panel = wx.Panel(self, wx.ID_ANY)
self.currentDirectory = os.getcwd()
saveFileDlgBtn = wx.Button(panel, label="Show SAVE FileDialog")
saveFileDlgBtn.Bind(wx.EVT_BUTTON, self.onSaveFile)
# put the buttons in a sizer
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(saveFileDlgBtn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
#----------------------------------------------------------------------
def onSaveFile(self, event):
"""
Create and show the Save FileDialog
"""
dlg = wx.FileDialog(
self, message="Save file as ...",
defaultDir=self.currentDirectory,
defaultFile="", wildcard=wildcard, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
print "You chose the following filename: %s" % path
dlg.Destroy()
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
I don't see anything wrong with your saving approach. In most cases, it's better to use Python's low level operators instead of using wxPython's. I would use Python's with operator though as that follows the newer idiom better:
with open(os.path.join(self.dirname, self.filename), 'w') as filehandle:
filehandle.write(contents)

wxPython: make variables available to an imported class?

I have written a wxPython GUI which assigns some variables upon a button click.
def OnGo(self, event):
inputdatadirectory = self.txtbx1.GetValue()
outputsavedirectory = self.txtbx2.GetValue()
mcadata = self.txtbx3.GetValue()
current_dir = os.getcwd()
execfile(current_dir+"\\aEDXD.py")
I then run execfile, and the executed file imports and runs a class from another file.
How can I make the variables I've defined through my GUI available to the imported class?
Yes you can, although it will probably be difficult to debug. Here is a silly example:
import wx
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Test")
panel = wx.Panel(self)
btn = wx.Button(panel, label="Go")
btn.Bind(wx.EVT_BUTTON, self.onGo)
self.Show()
#----------------------------------------------------------------------
def onGo(self, event):
""""""
foobar = "This is a test!"
execfile("foo.py")
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
And here is the foo.py file.
print foobar
If you run the wxPython script, it will execute foo.py which has access to the locals and globals in the wxPython script. You won't be able to run foo.py by itself though. See the following:
Execute a file with arguments in Python shell
https://docs.python.org/2/library/functions.html#execfile

wxPython event starts up at startup

So I am a complete beginner at python and usually code in C/C++ or Java. In the process of developing a GUI my events keep getting called at the start of the program running instead of when I click the button. (i know my indents are wrong because I needed to put it into a code block). How do I make my events only get called when I left click the button?
def __init__(self, parent, title):
super(QuadDash, self).__init__(parent, title=title, size=(1024, 780))
self.SetBackgroundColour("white")
pnl = wx.Panel(self)
cbtn = wx.Button(pnl, label='Start Quad', pos=(20,30))
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event(), cbtn)
self.Show(True)
def start_quad_event(self):
dlg = wx.MessageDialog(self, "Test", "ABC", wx.YES_NO | wx.ICON_QUESTION)
dlg.ShowModal()
if __name__ == '__main__':
app = wx.App()
qd = QuadDash(None, title='QuadCopter Dashboard')
app.MainLoop()
The offending line is:
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event(), cbtn)
You actually call start_quad_event() and pass the result to bind().
The correct line is:
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event, cbtn)
Note: No parentheses. Here you actually pass the function to bind().

wxpython: redirect text dynamically to text control box

The sampled code below is for the GUI I am currently making. I have two problems. The first problem is when I redirect the python output using the RedirectText class, the output is printed on the TextCtrl box ('tc2') only when the loop is finished, for that particular iteration. How do I get the text to print as it goes through the loop and not when its finished?
My second problem is i'm using a compiled fortran file EHSS_updated_atoms.out the output from this file prints to the terminal and not to the text control box. How do I get the the output from the compiled fortran file to the text control box? as it is printed to the terminal.
import wx
class RedirectText(object):
def __init__(self,aWxTextCtrl):
self.out=aWxTextCtrl
def write(self,string):
self.out.WriteText(string)
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
self.InitUI()
self.Centre()
self.Show()
self.SetSize((800, 600))
self.SetTitle('Hello')
def InitUI(self):
import sys
vbox = wx.BoxSizer(wx.VERTICAL)
hbox5 = wx.BoxSizer(wx.HORIZONTAL)
btn1 = wx.Button(panel, label='Run prog')
hbox5.Add(btn1)
btn2 = wx.Button(panel, label='Stop prog')
hbox5.Add(btn2, flag=wx.LEFT|wx.BOTTOM, border=5)
vbox.Add(hbox5, flag=wx.ALIGN_CENTER|wx.RIGHT, border=10)
self.Bind(wx.EVT_BUTTON, self.prog, btn1)
#self.Bind(wx.EVT_BUTTON, self.cancel_prog, btn2)
hbox3 = wx.BoxSizer(wx.HORIZONTAL)
tc2 = wx.TextCtrl(panel, style=(wx.TE_MULTILINE|wx.TE_READONLY))
hbox3.Add(tc2, proportion=1, flag=wx.EXPAND)
vbox.Add(hbox3, proportion=1, flag=wx.LEFT|wx.RIGHT|wx.EXPAND,border=10)
redir=RedirectText(tc2)
sys.stdout=redir
sys.stderr=redir
panel.SetSizer(vbox)
def prog(self, evt):
import os
import shutil
import subprocess
from mobcal_prep import mobcal
extension = '.pdb'
list_of_files = [file for file in os.listdir('%s' % _selectedDir) if file.lower().endswith(extension)]
prep = mobcal()
for pdb in list_of_files:
print '### Running PDB: %s ###' % pdb
prep.new_mobcal_format(pdb)
subprocess.call(['./EHSS_updated_atoms.out', pdb[:-4]+'_new_mobcal.mfj',pdb[:-4]+'_new_mobcal.out'])
prep.caluclate_average('%s_new_mobcal.out' % pdb[:-4], pdb)
subprocess.call(['rm', pdb[:-4]+'_new_mobcal.out'])
When dealing with graphical interfaces, have into account that the program is basically running into a loop (the "main loop"), which among other things triggers events, etc. That's the main thread of the program. If you do anything inside that thread (like calling EHSS_updated_atoms.out) that blocks the main loop for a while, there will be no changes on your interface until that is finished.
You can make this work by, for example, running the prog function in a separate thread. Check this forum post for a small example piece of code on how to do it. Note the hint to use wx.Yield() or wx.SafeYield() to make the interface more responsive.
Edit: forgot about the redirection.
You don't need to redirect the whole sys.stdout and sys.stderr. subprocess.call admits two arguments just for that, so if you pass redir to the prog function, you can do:
call(.........., stdout = redir, stderr = redir)
Edit:
For the redirection, try this instead:
p subprocess.Popen(['./EHSS_updated_atoms.out', pdb[:-4]+'_new_mobcal.mfj',pdb[:-4]+'_new_mobcal.out'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout:
redir.write(line)
p.wait()

Categories

Resources