I am sure this is a terribly stupid question. But I've used python for a year, i went through "Learn Python the hard way", I went through the Zetcode WxPython Tutorial, I looked through the WXPython TextCtrl Demo, and I've spent 2 days google searching for the answer to this seemingly simple question and I'm not figuring it out.
All I am trying to do is make a very simple test game where I have an input box, where the user types something like "get pineapple", then presses [ENTER], and send this phrase to a function I will make that processes the request, then sends the output to an output box that says something like "you cannot get the pineapple." Since I have coded lots of things at the office in python, and have coded full-featured financial models and games with VBA, I was sure this would be easy but it seems impossible. And WXPython is the obstacle.
If i put a string variable under "frame" or "panel" or under the class objects for TC1 or TC2, then they're treated as utterly alien and empty in the MainLoop, no matter where i put the variable it's not recognized. It seems impossible to use the same variable when making a function under the TextCtrl1 and TextCtrl2, or vice versa. I think I have to do something wwith "event.skip", or maybe make layers of passing things deep into the layers of WXPython and then pulling them all the way back out again, but I am not sure where to put it or how to do this.
Above all please tell me how I can figure out the answers to questions like this myself! I feel humiliated just asking it since it seems like it must be so easy, because if it were hard it would exist and be answered on this Q&A website.
I have all the GUI skeleton looking good, I have the "Enter" keypress event working, i have my multimedia set up and working fine, I know how i will be setting up my objects, I just need to be pointed in the right direction here. Even if you could just show me how to take input from one textctrl, and pass it unchanged to the output read-only textctrl, that would be perfect and i could figure out the rest. I could post the rest of my code here but I read somewhere that was bad manners.
EDIT: I am pasting the code here at the request of a would-be answerer.
Thank you in advance.
import wx
from random import randint
from pygame import mixer # Load the required library
mixer.init()
mixer.music.load('sog.ogg')
mixer.music.set_volume(1.0)
mixer.music.play()
RandomSound1 = mixer.Sound('CritHit.wav')
RandomSound2 = mixer.Sound('swallow2.wav')
RandomSound3 = mixer.Sound('thunder2.wav')
#instantiate class
class MusTogButt(wx.Button):
def __init__(self, *args, **kw):
super(MusTogButt, self).__init__(*args, **kw)
self.Bind(wx.EVT_BUTTON, self.MusicToggleFunction)
def MusicToggleFunction(self, mtf):
if mixer.music.get_busy() == True:
print "test, is playing"
mixer.music.stop()
else:
mixer.music.play()
class SoundTestButt(wx.Button):
def __init__(self, *args, **kw):
super(SoundTestButt, self).__init__(*args, **kw)
self.Bind(wx.EVT_BUTTON, self.PlayRandSound)
def PlayRandSound(self, mtf):
randsoundnum = randint (0,100)
if randsoundnum < 34:
RandomSound1.play()
elif randsoundnum < 68:
RandomSound2.play()
else:
RandomSound3.play()
class CPFInputter(wx.TextCtrl):
def __init__(self, *args, **kw):
super(CPFInputter, self).__init__(*args, **kw)
self.Bind(wx.EVT_COMMAND_ENTER, self.TextEntryFunction)
def TextEntryFunction(self, mtf):
print "you pressed enter"
class Example(wx.Frame):
def __init__(self, parent, title):
super(Example, self).__init__(parent, title=title,
size=(800, 600))
self.InitUI()
self.Centre()
self.Show()
def InitUI(self):
panel = wx.Panel(self)
#--------------------------------------------------------------
#This is an event handler. It handles the pressing of Enter, only.
def UserInputtedCommand(self):
keycode = self.GetKeyCode()
if keycode == wx.WXK_RETURN or keycode == wx.WXK_NUMPAD_ENTER:
print self.GetValue()
hbox = wx.BoxSizer(wx.HORIZONTAL)
fgs = wx.FlexGridSizer(3, 2, 9, 25)
# 3 rows
# 2 columns
# 9 vert gap
# 25 horizonatl gap
OutBoxLabel = wx.StaticText(panel, label="Outbox") #notice that these do NOT have wx.expand and they do NOT expand when the window is sized.
InBoxLabel = wx.StaticText(panel, label="Input:") #notice that these do NOT have wx.expand and they do NOT expand when the window is sized.
#make a bunch of input text controls, under the main panel.
InTC = wx.TextCtrl(panel)
InTC.Bind(wx.EVT_KEY_DOWN, UserInputtedCommand)
OutTC = wx.TextCtrl(panel, style=wx.TE_MULTILINE)
MusicToggle = MusTogButt(panel, label="Toggle Music Playback")
SoundTester = SoundTestButt(panel, label="Play Random Sound")
#Use AddMany AFTER you've built all your widgets with their specifications and put them in objects.
fgs.AddMany([(OutBoxLabel), (OutTC, 1, wx.EXPAND),
(InBoxLabel), (InTC, 1, wx.EXPAND), (MusicToggle), (SoundTester)])
fgs.AddGrowableRow(0, 1)
fgs.AddGrowableCol(1, 1)
# So, in other words, the 1st and second textboxes can grow horizontally, and the 3rd and final textbox can grow horizontally and vertically.
#lastly, add the FGS to the main hbox.
hbox.Add(fgs, proportion=1, flag=wx.ALL|wx.EXPAND, border=15)
#...and set sizer.
panel.SetSizer(hbox)
if __name__ == '__main__':
app = wx.App()
Example(None, title='CPF_FunGame2_MediaTest')
print "cpf_Fungame2_mediatest_running"
app.MainLoop()
Below creates two text controls, when you type something in the first and press enter it shows up in the second control.
In your code you are missing the style=wx.TE_PROCESS_ENTER.
# -*- coding: utf-8 -*-
#!/usr/bin/env python
import wx
import wx.lib.sized_controls as sc
class AFrame(sc.SizedFrame):
def __init__(self, *args, **kwds):
super(AFrame, self).__init__(*args, **kwds)
pane = self.GetContentsPane()
self.tc1 = wx.TextCtrl(pane, style=wx.TE_PROCESS_ENTER)
self.tc2 = wx.TextCtrl(pane)
self.tc1.Bind(wx.EVT_TEXT_ENTER, self.onTc1Enter)
def onTc1Enter(self, Evt):
self.tc2.ChangeValue(self.tc1.GetValue())
if __name__ == "__main__":
import wx.lib.mixins.inspection as WIT
app = WIT.InspectableApp()
f = AFrame(None)
f.Show()
app.MainLoop()
I suspect that your issue has to do with the scope of your variables, but without more detail I can only speculate what the issue might be. You could have this handled one of a few ways:
Pass the object of the output box to this function, then you can use its methods to display the result.
Return the value to allow it to be used at a greater scope than it was originally at.
When you bind to the TextCtrl, try this:
self.InTC.Bind(wx.EVT_TEXT, UserInputtedCommand)
def UserInputtedCommand (self, event):
Line = self.InTC.GetValue()
Related
I would like to have two (I will add more later) panels that occupy the same space within the frame and for them to be shown/hidden when the respective button is pressed on the toolbar, "mListPanel" should be the default. Currently the settings panel is shown when the application is launched and the buttons don't do anything. I've searched and tried lots of stuff for hours and still can't get it to work. I apologise if it's something simple, I've only started learning python today.
This is what the code looks like now:
import wx
class mListPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
#wx.StaticText(self, -1, label='Search:')#, pos=(10, 3))
#wx.TextCtrl(self, pos=(10, 10), size=(250, 50))
class settingsPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
class bifr(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Title")
self.listPanel = mListPanel(self)
self.optPanel = settingsPanel(self)
menuBar = wx.MenuBar()
fileButton = wx.Menu()
importItem = wx.Menu()
fileButton.AppendMenu(wx.ID_ADD, 'Add M', importItem)
importItem.Append(wx.ID_ANY, 'Import from computer')
importItem.Append(wx.ID_ANY, 'Import from the internet')
exitItem = fileButton.Append(wx.ID_EXIT, 'Exit')
menuBar.Append(fileButton, 'File')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.Quit, exitItem)
toolBar = self.CreateToolBar()
homeToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Home', wx.Bitmap('icons/home_icon&32.png'))
importLocalToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Import from computer', wx.Bitmap('icons/comp_icon&32.png'))
importToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'Import from the internet', wx.Bitmap('icons/arrow_bottom_icon&32.png'))
settingsToolButton = toolBar.AddLabelTool(wx.ID_ANY, 'settings', wx.Bitmap('icons/wrench_plus_2_icon&32.png'))
toolBar.Realize()
self.Bind(wx.EVT_TOOL, self.switchPanels(), settingsToolButton)
self.Bind(wx.EVT_TOOL, self.switchPanels(), homeToolButton)
self.Layout()
def switchPanels(self):
if self.optPanel.IsShown():
self.optPanel.Hide()
self.listPanel.Show()
self.SetTitle("Home")
elif self.listPanel.IsShown():
self.listPanel.Hide()
self.optPanel.Show()
self.SetTitle("Settings")
else:
self.SetTitle("Error")
self.Layout()
def Quit(self, e):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = bifr()
frame.Show()
app.MainLoop()
first off, i would highly suggest that you learn about wxpython sizers and get a good understanding of them (they're really not that hard the understand) as soon as possible before delving deeper into wxpython, just a friendly tip :).
as for your example, a few things:
when your'e not using sizers, you have to give size and position for every window or else they just wont show, so you'd have to change your panel classes to something like this (again this is only for demonstration, you should be doing this with wx.sizers, and not position and size):
class mListPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent,pos=(0,100),size=(500,500))
class settingsPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent,pos=(0,200),size (1000,1000))
further more, when binding an event it should look like this:
self.Bind(wx.EVT_TOOL, self.switchPanels, settingsToolButton)
self.Bind(wx.EVT_TOOL, self.switchPanels, homeToolButton)
notice how I've written only the name of the function without the added (), as an event is passed to it, you cant enter your own parameters to a function emitted from an event (unless you do it with the following syntax lambda e:FooEventHandler(paramaters))
and the event handler (function) should look like this:
def switchPanels(self, event):
if self.optPanel.IsShown():
self.optPanel.Hide()
self.listPanel.Show()
self.SetTitle("Home")
elif self.listPanel.IsShown():
self.listPanel.Hide()
self.optPanel.Show()
self.SetTitle("Settings")
else:
self.SetTitle("Error")
self.Layout()
there should always be a second parameter next to self in functions that are bind to event as the event object is passes there, and you can find its associated methods and parameters in the documentation (in this example it is the wx.EVT_TOOL).
This only happens on Linux (possible OS X also, can't test atm), works fine on Windows.
I have a wx.ProgressDialog that is spawned with the main thread. I send the work off to another thread, and it periodically calls back to a callback function in the main thread that will update the ProgressDialog or, at the end of the work, destroy it. However, I get an interesting message on Linux when this happens:
(python:12728): Gtk-CRITICAL **: IA__gtk_window_set_modal: assertion 'GTK_IS_WINDOW (window)' failed
The dialog does close, but if I try to spawn it again it looks like it's already almost finished. Sometimes a seg fault will follow this message as well.
I've tried to simulate it with a stripped down version here:
import wxversion
wxversion.select("2.8")
import wx
import sys
import threading
MAX_COUNT = 100
## This class is in a different area of the codebase and
class WorkerThread(threading.Thread):
def __init__(self, callback):
threading.Thread.__init__(self)
self.callback = callback
def run(self):
# simulate work done. IRL, this calls another function in another
# area of the codebase. This function would generate an XML document,
# which loops through a list of items and creates a set of elements for
# each item, calling back after each item. Here, we simply set up a for
# loop and simulate work with wx.MilliSleep
for i in xrange(MAX_COUNT):
print i
wx.MilliSleep(30)
wx.CallAfter(self.callback, i)
# Send done signal to GUI
wx.CallAfter(self.callback, -1)
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, pos=(150,150), size=(350,200))
panel = wx.Panel(self)
box = wx.BoxSizer(wx.VERTICAL)
m_btn = wx.Button(panel, wx.ID_ANY, "Run Stuff")
m_btn.Bind(wx.EVT_BUTTON, self.OnRunButton)
box.Add(m_btn, 0, wx.ALL, 10)
panel.SetSizer(box)
panel.Layout()
def OnRunButton(self, event):
self.progressDialog = wx.ProgressDialog("Doing work",
"Doing Work",
maximum=MAX_COUNT, parent=self,
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME)
self.worker(self.threadCallback)
self.progressDialog.ShowModal()
def worker(self, callback):
# This bit is in another part of the codebase originally. In the test,
# I could have added it to OnRunButton, but I wanted function calls to
# be similar between test and actual code
thread = WorkerThread(callback)
thread.start()
def threadCallback(self, info):
# We update based on position, or destroy if we get a -1
if info == -1:
self.progressDialog.Destroy()
else:
self.progressDialog.Update(info)
app = wx.App(redirect=False)
top = Frame("ProgressDialog Test")
top.Show()
app.MainLoop()
(we select 2.8, but ideally any fix should work in both 2.8 and 3.0. I actually haven't been able to test it in 3.0 in linux due to a bad 3.0 build)
This does a good job at representing the issue: works fine in Windows, but seg fault when it tries to destroy the progress dialog. However, I can't get the example to show the GTK_IS_WINDOW
Ive tried searching for solutions. I've read that it might be due to the fact that the worker thread finishes too quickly, and thus leaves the GUI with some messages in it's queue. I'm not sure I completely understand this (never got the hang of Yields and messages, etc), but what I believe this to mean is that when the worker is at 100%, the ProgressDialog (being slower), might only be at 75%, and still has the extra 25% of messages to use to "Update" the GUI, but instead gets destroyed.
I'd like some clarification on if I'm understanding that correctly or not.
Also, I believe .Hide() works as a work around, but I'd like to Destroy it instead because that's the proper thing to do.
Regardless, any help would be greatly appreciated. =)
I've tried your code, also many modifications been tried to overcome this issue, but failed. Anyway, I've created the following wxPython script to fulfill your purpose, see below:
import wxversion
wxversion.select("2.8") # version 3.0 works, too.
import wx
import sys
import threading
import time
MAX_COUNT = 200
class WorkerThread(threading.Thread):
def __init__(self, target, countNum):
threading.Thread.__init__(self, target = target)
self.setDaemon(True)
self.cnt = countNum
self.target = target
self.pb = self.target.pb
def run(self):
for i in xrange(self.cnt):
print i+1
wx.MilliSleep(50)
wx.CallAfter(self.pb.SetValue, i+1)
wx.CallAfter(self.target.MakeModal, False)
wx.CallAfter(self.target.Close)
class ProgressBarFrame(wx.Frame):
def __init__(self, parent, title, range = 100) :
wx.Frame.__init__(self, parent = parent, title = title)
self.range = range
self.createProgressbar()
self.SetMinSize((400, 10))
self.Centre()
self.Show()
self.t0 = time.time()
self.elapsed_time_timer.Start(1000)
def createProgressbar(self):
self.pb = wx.Gauge(self)
self.pb.SetRange(range = self.range)
self.elapsed_time_st = wx.StaticText(self, label = 'Elapsed Time:')
self.elapsed_time_val = wx.StaticText(self, label = '00:00:00')
vbox_main = wx.BoxSizer(wx.VERTICAL)
hbox_time = wx.BoxSizer(wx.HORIZONTAL)
hbox_time.Add(self.elapsed_time_st, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5)
hbox_time.Add(self.elapsed_time_val, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5)
vbox_main.Add(self.pb, 0, wx.EXPAND | wx.ALL, 5)
vbox_main.Add(hbox_time, 0, wx.EXPAND | wx.ALL, 5)
self.SetSizerAndFit(vbox_main)
self.elapsed_time_timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.onTickTimer, self.elapsed_time_timer)
def onTickTimer(self, event):
fmt='%H:%M:%S'
self.elapsed_time_val.SetLabel(time.strftime(fmt, time.gmtime(time.time()-self.t0)))
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, pos=(150,150), size=(350,200))
panel = wx.Panel(self)
box = wx.BoxSizer(wx.VERTICAL)
m_btn = wx.Button(panel, wx.ID_ANY, "Run Stuff")
self.Bind(wx.EVT_BUTTON, self.OnRunButton, m_btn)
box.Add(m_btn, 0, wx.ALL, 10)
panel.SetSizer(box)
def OnRunButton(self, event):
self.progressbar = ProgressBarFrame(self, 'Working Processing', MAX_COUNT)
self.progressbar.MakeModal(True)
worker = WorkerThread(self.progressbar, MAX_COUNT)
worker.start()
app = wx.App(redirect=False)
top = Frame("ProgressDialog Test")
top.Show()
app.MainLoop()
I'm using wx.Gauge to do what wx.ProgressDialog does, as well as an additional wx.Timer to show the elapsed time. MakeModal() method is used to mimic the ShowModal effect which is the default style that Dialog shows, do not forget to release the Modal status by MakeModal(False) or the frame would be freezed. You can add more stuff in the ProgressBarFrame class.
I'm thinking the segment fault error may arise from the events calling, especially when multithreading issue is involved, maybe carefully inspect into the wx.ProgressDialog class would show some clue.
Disclaimer: Perhaps I've gone about this incorrectly, but Im only about 24 hours into GUI development with wxpython. So general advice is appreciated.
Goal: I'm trying to familiarize myself with wxpython by making a simple screen saver.
Problem: Im trying to bind both mouse movement, and keyboard movement (aka trying to capture ANY user input) so it Destroys() the app on event (typical screensaver behavior). So far, my mouse events are being captured and it destroys properly, but my keyboard events are not (pound on my keyboard as I might!). I've tried EVT_CHAR and CHAR_HOOK as other SO posts have recommended. I've even go as far to see where my focus is - as I think that this is my problem (notice the line has self.SetFocus() - if you remove this, and move the mouse, the returned "self.FindFocus()" thats triggered by mouse movement events is returning None... with the SetFocus() it is now returning my class of SpaceFrame).
Question: Why can't I capture key strokes and activate my keyboardMovement() method? Interestingly enough, the example from here works just fine for the keyboard events down/up. So I'm 100% it's user error.
import wx
import random
MAX_INVADERS = 10
INVADERS_COLORS = ["yellow_invader",
"green_invader",
"blue_invader",
"red_invader"]
class SpaceFrame(wx.Frame):
def __init__(self):
"""
The generic subclassed Frame/"space" window. All of the invaders fall
into this frame. All animation happens here as the parent window
as well.
"""
wx.Frame.__init__(self, None, wx.ID_ANY, "Space Invaders", pos=(0, 0))
self.SetFocus()
self.Bind(wx.EVT_MOTION, self.mouseMovement)
self.Bind(wx.EVT_CHAR_HOOK, self.keyboardMovement)
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour('black')
self.SetBackgroundColour('black')
self.monitorSize = wx.GetDisplaySize()
for invader in range(0, MAX_INVADERS, 1):
randX = random.randint(0, self.monitorSize[0])
self.showInvader(coords=(randX, 0),
invader=random.choice(INVADERS_COLORS),
scale=(random.randint(2, 10)/100.0))
def mouseMovement(self, event, *args):
print self.FindFocus()
def keyboardMovement(self, event, *args):
self.Destroy()
def showInvader(self, coords=(0, 0), invader="green_invader", scale=.05):
"""
Displays an invader on the screen
"""
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.dropInvader, self.timer)
self.timer.Start(1000)
self.invader = wx.Bitmap("{0}.png".format(invader))
self.invader = wx.ImageFromBitmap(self.invader)
self.invader = self.invader.Scale((self.invader.GetWidth()*scale),
(self.invader.GetHeight()*scale),
wx.IMAGE_QUALITY_HIGH)
self.result = wx.BitmapFromImage(self.invader)
self.control = wx.StaticBitmap(self, -1, self.result)
self.control.SetPosition(coords)
self.panel.Show(True)
def moveInvader(self, coords):
self.control.SetPosition(coords)
def dropInvader(self, *args):
# print "this hit"
self.control.SetPosition((100, 600))
if __name__ == "__main__":
application = wx.PySimpleApp()
window = SpaceFrame()
window.ShowFullScreen(True, style=wx.FULLSCREEN_ALL)
application.MainLoop()
Research Done So Far: Maybe I missed something, but nothing stood out to me here.
Source1 - Mouse Vs Python examples
Source2 - SO similar problem (but not exactly my solution)
Source3 - WXpython forum/mailing list
Source4 - WXpython forum/mailing list
This is what I am trying to do:
I create a window and there is text that is displayed on it, as a user I click on the text,
example: the displayed text is.
'Hello World, I am a Python program.'
So if the user clicks words, I want it to generate an event and it would go into a function and I want to do something in the function (like changing the color of that word, so I also need to track which word I clicked)
I am not so sure how to do that, I could potentially make each word a button but that would be ugly.
import wx
def SomeListener(evt):
print "Got Event:",evt
print "My XY:",evt.GetX(),evt.GetY()
#youll have to figure out which word you clicked using x,y (note x,y relative to static text field)
a= wx.App(redirect=False)
f = wx.Frame(None,-1)
p = wx.Panel(f,-1)
t = wx.StaticText(p,-1,"Some Text")
t.Bind(wx.EVT_LEFT_DOWN,SomeListener)
f.Show()
a.MainLoop()
or using htmlwin ... but it underlines all the words... I wasnt able to figure out how to not do that
import wx
import wx.html
def OnClickWord(e):
print "You Clicked:",e.GetLinkInfo().GetHref()
return
class MyHtmlFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title)
html = wx.html.HtmlWindow(self)
#if "gtk2" in wx.PlatformInfo:
html.SetStandardFonts()
html.SetPage(
"<style>a {text-decoration: none;color: #000; }</style>" #sorry no css support :/
"Word1 word 2 wizard of oz.")
app = wx.PySimpleApp()
frm = MyHtmlFrame(None, "Simple HTML")
frm.Bind(wx.html.EVT_HTML_LINK_CLICKED,OnClickWord)
frm.Show()
app.MainLoop()
Joran's solution is fine if you don't mind each word looking like a bright blue underlined link.
For those interested in doing it WITHOUT blue underlined "hotlink" style text this can be accomplished in by setting up a RichText window. The code below is a code sample for this: it takes whatever you put in the "text" variable and processes it into the RichText window such that it looks like ordinary text but each word will throw an OnURL event when clicked. Neither the user nor the programmer has to worry about setting that up, just pass "text" to the URLtagger and process the OnURL calls however you want.
This example just passes the clicked word to the OnURL event, if you want unique identifiers for each word add them at the URLtagger method, note that the identifiers are wholly independent of the text shown so you can display text and receive numbers if you want.
import wx
import wx.richtext as rt
class RichTextFrame(wx.Frame):
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
self.rtc = rt.RichTextCtrl(self, style=wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER);
wx.CallAfter(self.rtc.SetFocus)
self.rtc.Bind(wx.EVT_TEXT_URL, self.OnURL)
def URLtagger(self, text):
for word in text.split():
self.rtc.BeginURL(word)
self.rtc.WriteText(" "+word+" ")
self.rtc.EndURL()
def OnURL(self, evt):
wx.MessageBox(evt.GetString(), "Word Clicked")
class TestPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
if rt.RichTextBuffer.FindHandlerByType(rt.RICHTEXT_TYPE_HTML) is not None:
return
rt.RichTextBuffer.AddHandler(rt.RichTextHTMLHandler())
rt.RichTextBuffer.AddHandler(rt.RichTextXMLHandler())
wx.FileSystem.AddHandler(wx.MemoryFSHandler())
self.win = RichTextFrame(self, -1, "wx.richtext.RichTextCtrl",
size=(700, 500),
style = wx.DEFAULT_FRAME_STYLE)
self.win.Show(True)
app = wx.App(0)
frame = wx.Frame(None)
panel = TestPanel(frame)
frame.Hide()
text = 'It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness.'
panel.win.URLtagger(text)
app.MainLoop()
I have a text box in wxpython (see below) that stores the name of the value as a variable.
I am trying to do two things:
After the answer is entered, I want to display another question, and assign the new answer to another variable, using the same or an idential TextEntryDialog window.
Ideally, from a user standpoint, they just see a prompt, type an answer (or select from a list), and then after hitting OK, the prompt will change, and they will type in a new answer (which will be assigned to a new variable).
So why am I trying to do this? So that after the end of this Q & A session, I can write all of the variables to a database using pyodbc (which I dont need to know about right now).
So could you please tell me how I can automatically generate new prompts once an answer has been entered without closing the app and losing the variable data? And is there anyway to automatically backup this variable data while the user is answering in case the app crashes? My question list is about 250 questions long, and I dont want all those variables lost if my application crashes (which they tend to do)
Thanks!
import wx
class applicationName(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Title', size=(300,200))
#create panel and button
panel = wx.Panel(self)
test = wx.TextEntryDialog(None, "What's your name?", 'Title', 'Enter name')
if test.ShowModal() == wx.ID_OK:
apples = test.GetValue()
wx.StaticText(panel, -1, apples, (10,10))
if __name__ =='__main__':
app = wx.PySimpleApp()
frame = applicationName(parent=None, id=-1)
frame.Show()
app.MainLoop()
I don't recommend creating and destroying 250 dialogs like the other fellow did. I would probably create a list or dict at the beginning of my program that would get appended to whenever the user enters an answer. Also in that event handler, I would reset the StaticText control with a new question. You might need to refresh the screen if your questions vary a lot in length, but I think that would be a lot better than showing hundreds of dialogs in a row.
EDIT - Added some example code below:
import wx
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.answers = {}
self.questions = ["What is your age?", "What is your weight?",
"Which of the following computer languages is the best ever: C++, PHP, Fortran, COBOL, Python?"]
self.nextQuestion = 0
self.question = wx.StaticText(panel, label="What is your name?")
self.answer = wx.TextCtrl(panel, value="")
submitBtn = wx.Button(panel, label="Submit")
submitBtn.Bind(wx.EVT_BUTTON, self.onSubmit)
sizer = wx.BoxSizer(wx.VERTICAL)
self.panelSizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.question, 0, wx.ALL, 5)
sizer.Add(self.answer, 0, wx.ALL|wx.EXPAND, 5)
sizer.Add(submitBtn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
self.panelSizer.Add(panel, 1, wx.EXPAND)
self.SetSizer(self.panelSizer)
#----------------------------------------------------------------------
def onSubmit(self, event):
""""""
self.answers[self.question.GetLabel()] = self.answer.GetValue()
self.question.SetLabel(self.questions[self.nextQuestion])
self.answer.SetValue("")
self.nextQuestion += 1
print self.answers
self.panelSizer.Fit(self)
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
Write a function that an entry dialog and displays and returns the value the user entered
Put it in a for loop
You can even do something like:
answers = [getanswer(q) for q in questions]
getanswer could look like:
def getanswer(q):
test = wx.TextEntryDialog(None, *q)
if test.ShowModal() == wx.ID_OK:
return test.GetValue() # returns None the user didn't select OK.
questions can contain lists or tuples of the stuff you want to pass to the constructor of wx.TextEntryDialog.