Mouse events on text in Python using wxPython - python

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()

Related

wx.ComboCtrl with wx.ListCtrl highlight background and sizing

I am building a wx.ComboCtrl with a wx.ListCtrl attached. The reason for doing this is because I want to set the foreground colour of the choices (the colour shows the user the status of the item). I want these colours to show when the box is dropped down and when a user has made a selection.
The problem I run into is that on Linux (Ubuntu 20.04), after a selection was made, the background colour of the wx.ComboCtrl remains blue (and the foreground colour remains white), even if I move focus to another widget. It doesn't matter which colour I set for the text to be displayed on the ComboCtrl, it remains white text with a blue background. See screenshot.
I can only get it to show me the default (gray) background with my selected foreground colour if I move the focus to another window and then back to my own window.
In Windows this doesn't happen: after selecting an item, the background colour of the ComboCtrl is default (gray), however it does show a little dotted line around the selection. See screenshot.
Here is the modified demo code that I am using to reproduce the issue. The comments in the code are left overs from some of the things I tried.
#!/usr/bin/env python
import wx
import os
#----------------------------------------------------------------------
#----------------------------------------------------------------------
# This class is used to provide an interface between a ComboCtrl and the
# ListCtrl that is used as the popoup for the combo widget.
class ListCtrlComboPopup(wx.ComboPopup):
def __init__(self):
wx.ComboPopup.__init__(self)
self.lc = None
def AddItem(self, txt, _colour):
self.lc.InsertItem(self.lc.GetItemCount(), txt)
_entry = self.lc.GetItem(self.lc.GetItemCount() - 1)
_entry.SetTextColour(_colour)
#_entry.SetItemTextColour(_colour)
self.lc.SetItem(_entry)
def OnMotion(self, evt):
item, flags = self.lc.HitTest(evt.GetPosition())
if item >= 0:
self.lc.Select(item)
self.curitem = item
def OnLeftDown(self, evt):
self.value = self.curitem
self.Dismiss()
# The following methods are those that are overridable from the
# ComboPopup base class. Most of them are not required, but all
# are shown here for demonstration purposes.
# This is called immediately after construction finishes. You can
# use self.GetCombo if needed to get to the ComboCtrl instance.
def Init(self):
self.value = -1
self.curitem = -1
# Create the popup child control. Return true for success.
def Create(self, parent):
self.lc = wx.ListCtrl(parent, style=wx.LC_SINGLE_SEL | wx.SIMPLE_BORDER | wx.LC_REPORT | wx.LC_NO_HEADER)
self.lc.InsertColumn(0, '')
self.lc.Bind(wx.EVT_MOTION, self.OnMotion)
self.lc.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
return True
# Return the widget that is to be used for the popup
def GetControl(self):
return self.lc
# Called just prior to displaying the popup, you can use it to
# 'select' the current item.
def SetStringValue(self, val):
idx = self.lc.FindItem(-1, val)
if idx != wx.NOT_FOUND:
self.lc.Select(idx)
# Return a string representation of the current item.
def GetStringValue(self):
if self.value >= 0:
return self.lc.GetItemText(self.value)
return ""
# Called immediately after the popup is shown
def OnPopup(self):
wx.ComboPopup.OnPopup(self)
# Called when popup is dismissed
def OnDismiss(self):
print (self.GetStringValue())
wx.ComboPopup.OnDismiss(self)
# This is called to custom paint in the combo control itself
# (ie. not the popup). Default implementation draws value as
# string.
def PaintComboControl(self, dc, rect):
wx.ComboPopup.PaintComboControl(self, dc, rect)
# Receives key events from the parent ComboCtrl. Events not
# handled should be skipped, as usual.
def OnComboKeyEvent(self, event):
wx.ComboPopup.OnComboKeyEvent(self, event)
# Implement if you need to support special action when user
# double-clicks on the parent wxComboCtrl.
def OnComboDoubleClick(self):
wx.ComboPopup.OnComboDoubleClick(self)
# Return final size of popup. Called on every popup, just prior to OnPopup.
# minWidth = preferred minimum width for window
# prefHeight = preferred height. Only applies if > 0,
# maxHeight = max height for window, as limited by screen size
# and should only be rounded down, if necessary.
def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
return wx.ComboPopup.GetAdjustedSize(self, minWidth, prefHeight, maxHeight)
# Return true if you want delay the call to Create until the popup
# is shown for the first time. It is more efficient, but note that
# it is often more convenient to have the control created
# immediately.
# Default returns false.
def LazyCreate(self):
return wx.ComboPopup.LazyCreate(self)
#----------------------------------------------------------------------
class MyTestPanel(wx.Panel):
def __init__(self, parent, log):
self.log = log
wx.Panel.__init__(self, parent, -1)
txt = wx.TextCtrl(self, wx.ID_ANY, pos=(100,100))
comboCtrl = wx.ComboCtrl(self, wx.ID_ANY, "Third item", (10,10), size=(200,-1), style=wx.CB_READONLY)
popupCtrl = ListCtrlComboPopup()
# It is important to call SetPopupControl() as soon as possible
comboCtrl.SetPopupControl(popupCtrl)
# Populate using wx.ListView methods
popupCtrl.AddItem("First Item", [255, 127, 0])
popupCtrl.AddItem("Second Item", [192, 127, 45])
popupCtrl.AddItem("Third Item", [25, 223, 172])
#popupCtrl.GetAdjustedSize(100, 35, 100)
#comboCtrl.SetTextColour(_colour)
comboCtrl.SetForegroundColour(wx.Colour(235, 55, 55))
#----------------------------------------------------------------------
def runTest(frame, nb, log):
win = MyTestPanel(nb, log)
return win
#----------------------------------------------------------------------
overview = """<html><body>
<h2><center>wx.combo.ComboCtrl</center></h2>
A combo control is a generic combobox that allows a totally custom
popup. In addition it has other customization features. For instance,
position and size of the dropdown button can be changed.
</body></html>
"""
if __name__ == '__main__':
import sys,os
import run
run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
Question 1:
How can I make it so that once an item has been selected the appropriate text colour (the one I programmatically set) and default (gray) background colour is shown.
Question 2:
When dropping down the ComboCtrl, it is showing the ListCtrl, which has a single column only. You can see that the "Second item" on the list is not displayed entirely because the column is too narrow. How can I make it so that the column is always the same width as the widget itself, even when the ComboCtrl resizes (as a result of resizing the parent window)?
Question 3:
Not overly important, but while we are on the subject: is there a way to get rid of the little dotted box that is shown around the selected item when running this in Windows?
In advance, thank you very much for your thoughts and ideas on this.
Marc.

Multi-Line Combobox in Tkinter

Is it possible to have a multi-line text entry field with drop down options?
I currently have a GUI with a multi-line Text widget where the user writes some comments, but I would like to have some pre-set options for these comments that the user can hit a drop-down button to select from.
As far as I can tell, the Combobox widget does not allow changing the height of the text-entry field, so it is effectively limited to one line (expanding the width arbitrarily is not an option). Therefore, what I think I need to do is sub-class the Text widget and somehow add functionality for a drop down to show these (potentially truncated) pre-set options.
I foresee a number of challenges with this route, and wanted to make sure I'm not missing anything obvious with the existing built-in widgets that could do what I need.
Terry's feedback made it clear that there was no simple way to solve this, so I created a custom class which wraps a Text and a Button into a frame, with a Toplevel containing a Listbox spawned by the button's callback function. I added a couple of "nice-to-have" features, like option highlighting within the Listbox, and I mapped bindings of the main widget onto the internal Text widget to make it easier to work with. Please leave a comment if there's any glaring bad practices here; I'm definitely still pretty inexperienced! But I hope this helps anybody else who's looking for a multi-line combobox!
class ComboText(tk.Frame):
def __init__(self, parent=None, **kwargs):
super().__init__(parent)
self.parent = parent
self._job = None
self.data = []
self['background'] = 'white'
self.text = tk.Text(self, **kwargs)
self.text.pack(side=tk.LEFT, expand=tk.YES, fill='x')
symbol = u"\u25BC"
self.button = tk.Button(self,width = 2,text=symbol, background='white',relief = 'flat', command = self.showOptions)
self.button.pack(side=tk.RIGHT)
#pass bindings from parent frame widget to the inner Text widget
#This is so you can bind to the main ComboText and have those bindings
#apply to things done within the Text widget.
#This could also be applied to the inner button widget, but since
#ComboText is intended to behave "like" a Text widget, I didn't do that
bindtags = list(self.text.bindtags())
bindtags.insert(0,self)
self.text.bindtags(tuple(bindtags))
def showOptions(self):
#Get the coordinates of the parent Frame, and the dimensions of the Text widget
x,y,width,height = [self.winfo_rootx(), self.winfo_rooty(), self.text.winfo_width(), self.text.winfo_height()]
self.toplevel = tk.Toplevel()
self.toplevel.overrideredirect(True) #Use this to get rid of the menubar
self.listbox = tk.Listbox(self.toplevel,width=width, height =len(self.data))
self.listbox.pack()
#Populate the options in the listbox based on self.data
for s in self.data:
self.listbox.insert(tk.END,s)
#Position the Toplevel so that it aligns well with the Text widget
list_height = self.listbox.winfo_reqheight()
self.toplevel.geometry("%dx%d+%d+%d" % (width, list_height, x, y+height))
self.listbox.focus_force()
self.listbox.bind("<Enter>", self.ListboxHighlight)
self.listbox.bind("<Leave>",self.stopListboxHighlight)
self.listbox.bind("<Button-1>",self.selectOption)
self.toplevel.bind("<Escape>", self.onCancel)
self.toplevel.bind("<FocusOut>", self.onCancel)
def ListboxHighlight(self,*ignore):
#While the mouse is moving within the listbox,
#Highlight the option the mouse is over
x,y = self.toplevel.winfo_pointerxy()
widget = self.toplevel.winfo_containing(x,y)
idx = self.listbox.index("#%s,%s" % (x-self.listbox.winfo_rootx(),y-self.listbox.winfo_rooty()))
self.listbox.selection_clear(0,100) #very sloppy "Clear all"
self.listbox.selection_set(idx)
self.listbox.activate(idx)
self._job = self.after(25,self.ListboxHighlight)
def stopListboxHighlight(self,*ignore):
#Stop the recurring highlight function.
if self._job:
self.after_cancel(self._job)
self._job = None
def onCancel(self,*ignore):
#Stop callback function to avoid error once listbox destroyed.
self.stopListboxHighlight()
#Destroy the popup Toplevel
self.toplevel.destroy()
def selectOption(self,event):
x,y = [event.x,event.y]
idx = self.listbox.index("#%s,%s" % (x,y))
if self.data:
self.text.delete('1.0','end')
self.text.insert('end',self.data[idx])
self.stopListboxHighlight()
self.toplevel.destroy()
self.text.focus_force()
def setOptions(self,optionList):
self.data = optionList
#Map the Text methods onto the ComboText class so that
#the ComboText can be treated like a regular Text widget
#with some other options added in.
#This was necessary because ComboText is a subclass of Frame, not Text
def __getattr__(self,name):
def textMethod(*args, **kwargs):
return getattr(self.text,name)(*args, **kwargs)
return textMethod
if __name__ == '__main__':
root = tk.Tk()
ct = ComboText(root, width = 50, height = 3)
ct.pack()
ct.setOptions(['Option %d' % i for i in range (0,5)])
root.mainloop()
I don't think you are missing anything. Note that ttk.Combobox is a composite widget. It subclasses ttk.Entry and has ttk.Listbox attached.
To make multiline equivalent, subclass Text. as you suggested. Perhaps call it ComboText. Attach either a frame with multiple read-only Texts, or a Text with multiple entries, each with a separate tag. Pick a method to open the combotext and methods to close it, with or without copying a selection into the main text. Write up an initial doc describing how to operate the thing.

wxPython - Process input from one TextCtrl and send output to another TextCtrl?

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()

Interpreting simple code to python code

I build a lot of very basic QT repetitive programs at work with QT python then compile them with py2exe. These are used by my coworkers at controllers for another program. I am wonder how I could build a python interpreter that would convert simple commands into actual python code similar to the way Processing converts a simplified code into java.
For example the "simple code" file may read:
slider('Distance', 'dist', 50, 100, 'int')
button('Apply Changes', 'apply')
which I would then interpret into a pyQT program form using types below:
slider(label, name, min, max, type)
button(label, name)
These would all be written to new python file, which when run would generate the appropriate form. The part I am stuck on is how to interpret the "simple code" into python code.
Thanks in advance
Solution #1
The code below is uses the regex and split idea by SPEN-zar to identify the widget type, then parse the inputs to generate the necessary outputs. Obviously, this will be expanded upon to generate real pyQT file, but this is the basic demonstrates the basic logic.
Thank you for the help.
import re
input = ["slider('Distance', 'dist', 50, 100, 'int')", "button('Apply Changes', 'apply')"]
pattern = r"([a-z]+)\s*\((.*)\)"
rexp = re.compile(pattern)
for line in input:
content = rexp.findall(line)
if content[0][0] == 'slider':
params = content[0][1].split(',')
name = params[0]
label = params[1]
minimum = float(params[2])
maximum = float(params[3])
print 'Slider Type: name-%s, label-%s, min-%f, max-%f' % (name, label, minimum, maximum)
elif content[0][0] == 'button':
params = content[0][1].split(',')
name = params[0]
label = params[1]
print 'Button Type: name-%s, label-%s' % (name, label)
else:
print 'This widget type is not recognized'
Solution #2
After doing further research into blender's suggestion, I have modified the code below that uses a class to define a button. This class can then be easily added to the form as many times as needed. By building classes for all the types needed it would be easy to easy generate forms as well maintain and add to the library.
from PyQt4 import QtGui, QtCore
import sys
class Main(QtGui.QMainWindow):
def __init__(self, parent = None):
super(Main, self).__init__(parent)
# main button
self.addButton = QtGui.QPushButton('button to add other widgets')
self.addButton.clicked.connect(self.addWidget)
# scroll area widget contents - layout
self.scrollLayout = QtGui.QFormLayout()
# scroll area widget contents
self.scrollWidget = QtGui.QWidget()
self.scrollWidget.setLayout(self.scrollLayout)
# scroll area
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.scrollWidget)
# main layout
self.mainLayout = QtGui.QVBoxLayout()
# add all main to the main vLayout
self.mainLayout.addWidget(self.addButton)
self.mainLayout.addWidget(self.scrollArea)
# central widget
self.centralWidget = QtGui.QWidget()
self.centralWidget.setLayout(self.mainLayout)
# set central widget
self.setCentralWidget(self.centralWidget)
def addButton(self):
self.scrollLayout.addRow(Test())
class Test(QtGui.QWidget):
def __init__( self, parent=None):
super(Test, self).__init__(parent)
self.pushButton = QtGui.QPushButton('I am in Test widget')
self.pushButton.clicked.connect(self.testPush)
layout = QtGui.QHBoxLayout()
layout.addWidget(self.pushButton)
self.setLayout(layout)
def testPush(self):
print "The test button was pushed!"
app = QtGui.QApplication(sys.argv)
myWidget = Main()
for i in xrange(5):
myWidget.addButton()
myWidget.show()
app.exec_()
Have you tried just parsing it into a .ui format? That is XML, which is pretty straightforward.
Whichever output format you prefer, try PLY for the lexing and parsing. Otherwise, just use a regular expression to search for the substring with "(" ")" surrounding it and use .split(',') to get the arguments. That's my take.

How do you get checkbox selections from a CustomTreeCtrl

I'm working with a CustomTreeCtrl with checkboxes and I can't figure out how to determine which checkboxes are selected. I looked at http://xoomer.virgilio.it/infinity77/wxPython/Widgets/wx.TreeCtrl.html#GetSelection and put this together:
import string
import os
import sys
import wx
import wx.lib.agw.customtreectrl as CT
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "CustomTreeCtrl Demo")
custom_tree = CT.CustomTreeCtrl(self, agwStyle=wx.TR_DEFAULT_STYLE)
root = custom_tree.AddRoot("The Root Item")
for y in range(5):
last = custom_tree.AppendItem(root, "item %d" % y)
for z in range(5):
item = custom_tree.AppendItem(last, "item %d" % z, ct_type=1)
self.Bind(CT.EVT_TREE_ITEM_CHECKED, self.ItemChecked)
def ItemChecked(self, event):
print("Somebody checked something")
print(event.GetSelections())
app = wx.PySimpleApp()
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
When I check a box, I get the Traceback: "AttributeError: 'TreeEvent' object has no attribute 'GetSelections'" Any suggestions on how to read which boxes are selected would be great!
The event object in question doesn't have a GetSelections method. It does have a GetSelection, which will tell you which item was selected at that event. If you want to get all of the selected items inside ItemChecked, rename custom_tree to self.custom_tree and then you're allowed to call self.custom_tree.GetSelections() inside ItemChecked.
If in future you want to know what kind of methods are available for some event object, you can put print(dir(event)) in your handler.
The custom tree control doesn't have a method to get the checked items. One thing that you could do is create a self.checked_items list in your frame, and maintain it in your ItemChecked method. This list could hold either the string values for the items or the items themselves. For instance,
class MyFrame(wx.Frame):
def __init__(self, parent):
# ....
self.checked_items = []
# ....
def ItemChecked(self, event):
if event.IsChecked():
self.checked_items.append(event.GetItem())
# or to store the item's text instead, you could do ...
# self.checked_items.append(self.custom_tree.GetItemText(event.GetItem()))
else:
self.checked_items.remove(event.GetItem())
# or ...
# self.checked_items.remove(self.custom_tree.GetItemText(event.GetItem()))

Categories

Resources