Is there a possibility to give a Unit test a parameter, which is provided at runtime?
For example I've connected 5 COM-Ports to my machine. The test environment should select the correct COM-Port and run its test with it. So whats tested is not the actual python code, the python unit test tool is basically used to verify the communication established with by a python script. Depending on initial communication a number of tests with according parameters (e.g. the comport used) should be selected and added to the batch ( test suite ).
In the next step I want to iterate over an array containing COM-Ports and other optiones.
There is a decorator #parametrized. This solution looks nice and invokes the test like desired. But it is not possible, to use this at run time. The decorator is initiated when the script is loaded. The only workaround known to me is to use a global variable to pass the COM-Port to the TestCase. The tests then must be invoked by a test runner.
To sum up:
Is there a possibility to parametrize a test at run time?
This then could also be added to a testsuite to have a list of same tests only differ in their parameters.
Or an other alternative would be to assign the parameter in the setUpClass() and have anyhow to run several tests again, but with other parameters. Probably this is possible?
So, how do I set up tests with parameter in python with unit test during runtime?
Well the only way to find out if the COM port is correct is by communicating with it. What kind of COM port are we talking about? A 3D printer for example will send periodic commands signaling that it is ready for use, you could use this to detect if there is a 3D printer connected. In python you can easily get a list of available COM ports and try to connect to one of them. I once wrote an interface that could connect to any of an automatically generated list of available COM ports. Maybe you can use parts of it to complete your project:
import math
import serial
import wx
import threading
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title = "Hydroponics controller")
self.comport = ""
self.baudrate = 0
self.ser = serial.Serial()
self.p = wx.Panel(self)
nb = Notebook(self)
tab1 = connectionTab(nb)
tab2 = ControllerTab(nb)
nb.AddPage(tab1, "Connection")
nb.AddPage(tab2, "Controller")
sizer = wx.BoxSizer()
sizer.Add(nb, 1, wx.EXPAND)
self.p.SetSizer(sizer)
class Notebook(wx.Notebook):
def __init__(self, parent):
wx.Notebook.__init__(self, parent.p)
self.ser = parent.ser
class connectionTab(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.p = parent
comLabel = wx.StaticText(self, -1, "Serial port", pos = wx.Point(10, 10), size = wx.Size(64, 20))
self.comList = wx.ListBox(self, size = wx.Size(64, 128), choices = [], pos = wx.Point(10, 40), style = wx.LB_SINGLE)
baudrateLabel = wx.StaticText(self, -1, "Baudrate", pos = wx.Point(84, 10), size = wx.Size(64, 20))
self.baudrateList = wx.ListBox(self, size = wx.Size(64, 128), choices = ["9600", "115200", "250000"], pos = wx.Point(84, 40), style = wx.LB_SINGLE)
self.baudrateList.SetSelection(1)
self.refreshButton = wx.Button(self, -1, "Refresh", pos = wx.Point(158, 40))
self.refreshButton.Bind(wx.EVT_BUTTON, self.refreshButton_clicked)
self.connectButton = wx.Button(self, -1, "Connect", pos = wx.Point(158, 70))
self.connectButton.Bind(wx.EVT_BUTTON, self.connectButton_clicked)
self.statusLabel = wx.StaticText(self, -1, "", pos = wx.Point(158, 100))
self.refreshDevices()
def refreshButton_clicked(self, event):
self.refreshDevices()
def connectButton_clicked(self, event):
try:
self.p.ser = serial.Serial(self.comList.GetStringSelection(), self.baudrateList.GetStringSelection())
self.statusLabel.SetLabel("Connection succesful.")
except serial.serialutil.SerialException:
self.statusLabel.SetLabel("Connection failed.")
def refreshDevices(self):
self.availableDevices = []
self.comList.Clear()
for i in range(0,256):
try:
tmp = serial.Serial("COM{}".format(i))
self.comList.Append("COM{}".format(i))
except serial.serialutil.SerialException:
continue
if(self.comList.GetCount()):
self.comList.SetSelection(0)
class ControllerTab(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.parent = parent
self.threadStopped = True
self.t = threading.Thread(target = self.runSequence)
self.t.start()
ROWS = 3
ROWS_POS = [40, 370, 700]
COLUMNS = 10
COLUMNS_POS = [0, 88, 176, 264, 352, 440, 528, 616, 704, 792]
self.commandList = []
self.buttonList = []
for i in range(0, COLUMNS):
for j in range(0, ROWS):
self.buttonList.append(PlantButton(self, COLUMNS_POS[i], ROWS_POS[j], wx.Point(i * 110 + 10, j * 30 + 10)))
self.homeAllButton = wx.Button(self, -1, "Home all", pos = wx.Point(10, ROWS * 30 + 10))
self.homeAllButton.Bind(wx.EVT_BUTTON, self.homeAllButton_clicked)
self.dwellButton = wx.Button(self, -1, "Dwell 1s", pos = wx.Point(10, (ROWS + 1) * 30 + 10))
self.dwellButton.Bind(wx.EVT_BUTTON, self.dwellButton_clicked)
self.commandListBox = wx.ListBox(self, size = wx.Size(192, 512), choices = [], pos = wx.Point(10, (ROWS + 2) * 30 + 10))
self.startSequenceButton = wx.Button(self, -1, "Start sequence", pos = wx.Point(212, (ROWS + 2) * 30 + 10))
self.startSequenceButton.Bind(wx.EVT_BUTTON, self.startSequenceButton_clicked)
self.saveFileButton = wx.Button(self, -1, "Save to file", pos = wx.Point(212, (ROWS + 3) * 30 + 10))
self.saveFileButton.Bind(wx.EVT_BUTTON, self.saveFileButton_clicked)
self.deleteCommandButton = wx.Button(self, -1, "Delete selected command", pos = wx.Point(212, (ROWS + 4) * 30 + 10))
self.deleteCommandButton.Bind(wx.EVT_BUTTON, self.deleteCommandButton_clicked)
def homeAllButton_clicked(self, event):
self.commandList.append("G92 X200\nG1 X0 F6000\nG28 XYZ\n")
self.commandListBox.Append("Home all")
def dwellButton_clicked(self, event):
self.commandList.append("G4 P1000\n")
self.commandListBox.Append("Dwell for 1 second")
def startSequenceButton_clicked(self, event):
self.threadStopped = False
def saveFileButton_clicked(self, event):
file = open("sequence.gcode", "w")
for i in range(0, len(self.commandList)):
file.write(self.commandList[i])
file.close()
def deleteCommandButton_clicked(self, event):
index = self.commandListBox.GetSelection()
if(index == wx.NOT_FOUND):
return
self.commandList.pop(index)
self.commandListBox.Delete(index)
if(index == 0):
self.commandListBox.SetSelection(0)
else:
self.commandListBox.SetSelection(index - 1)
def runSequence(self):
while True:
if(self.threadStopped == False):
sequenceLength = len(self.commandList)
index = 0
self.parent.ser.write("G92 X100\nG1 X0 F6000\nM340 P3 S2000\nG28 XYZ\n")
while((index < sequenceLength) & (self.threadStopped == False)):
data = self.parent.ser.readline()
if(len(data) != 0):
print "READ: {}".format(data),
if(data == "ok 0\r\n"):
print "SENT: {}".format(self.commandList[index]),
self.parent.ser.write(self.commandList[index])
index += 1
print "Stopped"
self.threadStopped = True
class PlantButton:
def __init__(self, parent, x, y, buttonPos):
self.parent = parent
self.x = x
self.y = y
self.buttonPos = buttonPos
self.button = wx.Button(self.parent, -1, "({}, {})".format(self.x, self.y), pos = self.buttonPos)
self.button.Bind(wx.EVT_BUTTON, self.button_clicked)
def button_clicked(self, event):
self.parent.commandList.append("G1 X{} Y{} F6000\n".format(self.y, self.x))
self.parent.commandList.append("G1 Z70 F6000\nG4 P1000\nM340 P3 S2500\nG4 P500\nM340 P3 S2468\nG4 P1000\nG91\nG1 X-40 F6000\nG90\nG1 Z0 F6000\n")
self.parent.commandList.append("G1 X700 F6000\nG4 P1000\nM340 P3 S2000\n")
self.parent.commandListBox.Append("Go to position ({}, {})\n".format(self.x, self.y))
if __name__ == "__main__":
app = wx.App()
MainFrame().Show()
app.MainLoop()
Related
I'm trying to build an app that downloads a file and updates a wx.Gauge in real time with the download progress. Additionally, I would like to show the current downloaded size / total size and a real time updating download speed.
In my minimal code example, the progress variable, which would have a value between 0 and 100, is set to perc_value. The size and current speed text is set to progress_text.
I tried using tqdm, but their examples shows their own implementation of progress bar and speed. I couldn't find a variable/function from their library to give me those info.
Can you help me?
Thank you!
import wx
import requests
from threading import Thread
class DownloadThread(Thread):
def __init__(self, parent, url):
Thread.__init__(self)
self.parent = parent
self.url = url
self.start()
def run(self):
perc_value = 0
progress_text = ''
with open('file', 'wb') as f:
r = requests.get(self.url, stream=True)
for chunk in r.iter_content(4096):
f.write(chunk)
wx.CallAfter(self.parent.update_gauge, perc_value, progress_text)
class Main(wx.Frame):
def __init__(self, parent):
super().__init__(parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.gauge = wx.Gauge(self, -1)
self.progress_t = wx.StaticText(self, -1, "Testing")
sizer.Add(self.gauge, flag=wx.ALL, border=10)
sizer.Add(self.progress_t, flag=wx.ALL, border=10)
self.SetSizerAndFit(sizer)
self.start_download()
def start_download(self):
url = '.../file.zip'
DownloadThread(self, url)
def update_gauge(self, perc_value, progress_text):
self.gauge.SetValue(perc_value)
self.progress_t.SetLabel(progress_text)
app = wx.App()
Main(None).Show()
app.MainLoop()
Edit:
I have made some improvements. I found the best solution is not using tqdm. My only problem now is the download speed, which is inaccurate.
Edit2: Solved the problem. The corrected version is below!
import wx
import requests
from threading import Thread
class DownloadThread(Thread):
def __init__(self, parent, url):
Thread.__init__(self)
self.parent = parent
self.url = url
self.start()
def run(self):
perc_temp = 0
r = requests.get(self.url, stream=True)
self.total_length = r.headers.get('content-length')
with open('file.data', 'wb') as f:
if self.total_length is None: # no content length header
f.write(r.content)
else:
self.dl = 0
self.dl_temp = 0
self.total_length = int(self.total_length)
self.total_size = self.get_downloaded_value(self.total_length)
self.total_unit = self.get_unit(self.total_length)
print(self.total_length)
for data in r.iter_content(chunk_size=4096):
self.dl += len(data)
self.dl_temp += len(data)
f.write(data)
value = int(100 * self.dl / self.total_length)
if perc_temp != value:
perc_temp = value
wx.CallAfter(self.parent.update_gauge, value)
def get_progress_text(self):
downloaded = self.get_downloaded_value(self.dl)
unit_downloaded = self.get_unit(self.dl)
speed = self.get_downloaded_value(self.dl_temp)
speed_unit = self.get_unit(self.dl_temp)
text = f"{downloaded:.2f} {unit_downloaded} / {self.total_size:.2f} {self.total_unit}\t\t{speed:.2f} {speed_unit}/s"
self.dl_temp = 0
return text
def get_unit(self, size):
unit = ''
if size < 1024:
unit = 'B'
elif size < 1048576:
unit = 'KB'
else:
unit = 'MB'
return unit
def get_downloaded_value(self, size):
if size < 1024:
return size
elif size < 104876:
return size / 1024
else:
return size / 1048576
class Main(wx.Frame):
def __init__(self, parent):
super().__init__(parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.gauge = wx.Gauge(self, -1)
self.progress_t = wx.StaticText(self, -1, "Testing", size=(300, 23))
sizer.Add(self.gauge, flag=wx.ALL | wx.EXPAND, border=10)
sizer.Add(self.progress_t, flag=wx.ALL | wx.EXPAND, border=10)
self.SetSizerAndFit(sizer)
self.start_download()
def start_download(self):
url = 'https://portal.inmet.gov.br/uploads/dadoshistoricos/2008.zip'
self.thread = DownloadThread(self, url)
self.timer.Start(1000)
def update_gauge(self, perc_value):
self.gauge.SetValue(perc_value)
def OnTimer(self, event):
self.progress_t.SetLabel(self.thread.get_progress_text())
app = wx.App()
Main(None).Show()
app.MainLoop()
I am trying to run a progrom from the book "Remote Sensing Raster Programming" for calculating NDVI from raster dataset. Program reads two separate raster bands NIR and RED using GDAL and executes following equation NDVI = (NIR-RED)/(NIR+RED).
But it is producing error and GUI is not openning. I am trying to learn wxPython and I will appreciate any help to sort out this problem.
Error -
Traceback (most recent call last):
File "C:\Users\User\Desktop\ndvi.py", line 189, in <module>
frame = MyFrame(None)
File "C:\Users\User\Desktop\ndvi.py", line 48, in __init__
self.make_fb()
File "C:\Users\User\Desktop\ndvi.py", line 112, in make_fb
fileMode=wx.OPEN,
AttributeError: 'module' object has no attribute 'OPEN'
Code for the program
#!/usr/bin/python
# -*- coding: cp1252 -*-
import wx
import wx.lib.filebrowsebutton as filebrowse
import os
# For Image Processing
import numpy
from osgeo import gdalnumeric
from osgeo import gdal
from osgeo import gdal_array
from osgeo.gdalconst import *
# Define satellite bands
redchan = ''
nirchan = ''
# Define output file name
output = ''
# Define Info Message
overview = """Vegetation Index Processing.
Calculates vegetation indices based on biophysical parameters.
NDVI: Normalized Difference Vegetation Index
NDVI = (band 2 - band 1) / (band 2 + band 1)
NDVI = (NIR - Red) / (NIR + Red)
Rouse J., Haas R., Schell J. and Deering D. (1974).
Monitoring vegetation systems in the Great Plains
with ERTS. In Proceedings of the Third Earth
Resources Technology Satellite-1 Symposium, Greenbelt."""
class MyFrame(wx.Frame):
def __init__(self,parent, id=-1, title='Normalized Difference Vegetation Index Processing',
pos=(0,0),
size=(400,400),
style=wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, parent, id, title, pos, size, style)
self.lognull = wx.LogNull()
# Input Filenames
self.redchan = redchan
self.nirchan = nirchan
self.output = output
# Construct Interface
self.make_text()
self.make_buttons()
self.make_fb()
self.mbox = wx.BoxSizer(wx.VERTICAL)
self.mbox.Add((10,10))
self.mbox.Add(self.text, 1, wx.EXPAND|wx.CENTER, 10)
self.mbox.Add((10,10))
self.mbox.Add((10,10))
self.mbox.Add(self.cc2, 1, wx.EXPAND, 10)
self.mbox.Add(self.cc3, 1, wx.EXPAND, 10)
self.mbox.Add(self.cc6, 1, wx.EXPAND, 10)
self.mbox.Add((10,10))
self.mbox.Add((10,10))
self.mbox.Add(self.bbox, 1, wx.CENTER, 10)
self.mbox.Add((10,10))
self.SetSizer(self.mbox)
self.bindEvents()
# Process Equations, Handling and saving of output
def OnOK(self,event):
print "red: ", self.redchan, " nir:",self.nirchan, " out:", self.output
if(self.redchan==''):
self.OnFileInError()
elif(self.nirchan==''):
self.OnFileInError()
else:
self.redband = gdal_array.LoadFile(self.redchan)
self.nirband = gdal_array.LoadFile(self.nirchan)
# NDVI
self.result=self.ndvi(self.redband, self.nirband)
# prepare/create output file
tmp = gdal.Open(str(self.redchan))
geoT = tmp.GetGeoTransform()
proJ = tmp.GetProjection()
tmp = None
out = gdal_array.OpenArray(self.result )
out.SetGeoTransform( geoT )
out.SetProjection( proJ )
driver = gdal.GetDriverByName( 'GTiff' )
driver.CreateCopy( self.output, out )
self.Destroy()
def ndvi( self, redchan, nirchan ):
"""
Normalized Difference Vegetation Index
ndvi( redchan, nirchan )
"""
result = 1.0*( nirchan - redchan )
result /= 1.0*( nirchan + redchan )
return result
def OnFileInError(self):
dlg = wx.MessageDialog(self,
'Minimum files to add:\n\n Input files => Red and NIR \n One Output file',
'Error',wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
# Path+filename seek and set
def make_fb(self):
# get current working directory
self.dirnm = os.getcwd()
self.cc2 = filebrowse.FileBrowseButton(
self, -1, size=(50, -1), labelText='RED:',
startDirectory = self.dirnm,
fileMode=wx.OPEN,
changeCallback = self.fbbCallback2,
)
self.cc3 = filebrowse.FileBrowseButton(
self, -1, size=(50, -1), labelText='NIR:',
startDirectory = self.dirnm,
fileMode=wx.OPEN,
changeCallback = self.fbbCallback3
)
self.cc6 = filebrowse.FileBrowseButton(
self, -1, size=(50, -1), labelText='OUT File:',
startDirectory = self.dirnm,
fileMask='*.tif',
fileMode=wx.SAVE,
changeCallback = self.fbbCallback6
)
# Collect path+filenames
def fbbCallback2(self, evt):
self.redchan = str(evt.GetString())
def fbbCallback3(self, evt):
self.nirchan = str(evt.GetString())
def fbbCallback6(self, evt):
self.output = str(evt.GetString())
# Front text
def make_text(self):
self.text = wx.StaticText(self, -1, '''\n\tThis is a full Python +
WxPython application,\n\tprocessing NDVI through the use
of \n\tGDAL Python bindings and numpy''')
# Bottom buttons
def make_buttons(self):
self.bbox = wx.BoxSizer(wx.HORIZONTAL)
# OnOK
bmp0 = wx.ArtProvider.GetBitmap(wx.ART_TICK_MARK, wx.ART_TOOLBAR, (32,32))
self.b0 = wx.BitmapButton(self, 20, bmp0, (20, 20),
(bmp0.GetWidth()+50, bmp0.GetHeight()+10), style=wx.NO_BORDER)
self.b0.SetToolTip("Process")
self.bbox.Add(self.b0,1,wx.CENTER,10)
# OnCancel
bmp1 = wx.ArtProvider.GetBitmap(wx.ART_CROSS_MARK, wx.ART_TOOLBAR, (32,32))
self.b1 = wx.BitmapButton(self, 30, bmp1, (20, 20),
(bmp1.GetWidth()+50, bmp1.GetHeight()+10), style=wx.NO_BORDER)
self.b1.SetToolTip("Abort")
self.bbox.Add(self.b1,1,wx.CENTER,10)
# OnInfo
bmp2 = wx.ArtProvider.GetBitmap(wx.ART_HELP, wx.ART_TOOLBAR, (32,32))
self.b2 = wx.BitmapButton(self, 40, bmp2, (20, 20),
(bmp2.GetWidth()+50, bmp2.GetHeight()+10), style=wx.NO_BORDER)
self.b2.SetToolTip("Help/Info.")
self.bbox.Add(self.b2,1,wx.CENTER,10)
def bindEvents(self):
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
self.Bind(wx.EVT_BUTTON, self.OnOK, self.b0)
self.Bind(wx.EVT_BUTTON, self.OnCancel, self.b1)
self.Bind(wx.EVT_BUTTON, self.OnInfo, self.b2)
def OnCloseWindow(self, event):
self.Destroy()
def OnCancel(self, event):
self.Destroy()
def OnInfo(self,event):
dlg = wx.MessageDialog(self, overview,'Help', wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
class MainApp(wx.App):
def OnInit(self):
frame = MainFrame(None)
frame.Show(True)
self.SetTopWindow(frame)
return True
if __name__ == '__main__':
app = wx.App()
frame = MyFrame(None)
frame.Show()
app.MainLoop()
If you are using wxPython 4 (Phoenix), then you want to use wx.FD_OPEN instead of wx.OPEN. I'm not sure why that's not in the Migration Guide or the Classic Vs. Phoenix page though
I'm having problem when I subscribe message from pubsub in the childframe class. I can't figure out how write correct code which will show selects from the mainframe. I also can't pass definition row in the childframe class. I don't know how I specified my question I know it's hard to understand, but in this code I think you will find exactly what i was asking for. I'm new in python so plizz don't criticize
import wx
from wx.lib.pubsub import Publisher as pub
import pyodbc
cnxn = pyodbc.connect("DRIVER={SQL Server Native Client 11.0};SERVER=10.75.79.215;DATABASE=HUB_DATA;UID=sa;PWD=password")
cursor = cnxn.cursor()
class Mainframe(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
self.quote1 = wx.StaticText(self.panel, label = "id")
self.test=wx.StaticText(self.panel, label = "")
self.editquote1 = wx.TextCtrl(self.panel, size = (140, -1))
self.button = wx.Button(self.panel, label = "search")
#
self.windowSizer = wx.BoxSizer()
self.windowSizer.Add(self.panel, wx.ID_ANY, wx.ALL | wx.EXPAND)
#
self.sizer = wx.GridBagSizer(6, 6)
self.sizer.Add(self.quote1, (0, 0))
self.sizer.Add(self.editquote1, (0, 1))
self.sizer.Add(self.button, (3, 1))
self.sizer.Add(self.test, (4, 1))
#
self.border = wx.BoxSizer()
self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 6)
#
self.panel.SetSizerAndFit(self.border)
self.SetSizerAndFit(self.windowSizer)
#
self.button.Bind(wx.EVT_BUTTON, self.Onbutton and self.afterOnbutton)
def Onbutton(self,e):
var = self.editquote1.GetValue()
if len(var)==9:
self.test.SetLabel('True')
if len(var)==9:
cursor.execute("""SELECT COUNT(DPVLDT) as amount
FROM [HBGE_Reports].[dbo].[hub_DDJPFile] s
where exists (SELECT [ZGDCS]
FROM [HUB_DATA].[dbo].[SSCUSTP] d
where [ZGIDNO]=?
and
s.DPACS=d.ZGDCS)""",str(var))
raw = cursor.fetchone()
a = raw.amount
pub.sendMessage("amount", a)
cursor.execute("""declare #a varchar(20)
set #a=?
SELECT [DPVLDT]
,[DPCPDT]
,[DPACB]
,[DPACS]
,[DPACX]
,[DPCYCD]
,[DPDLCD]
,[RCY_AMOUNT]
,[LCY_AMOUNT]
,[DPBLBL]
,[DPNAR1]
,[DPNAR2]
,[DPNAR3]
,[DPNAR4]
FROM [HBGE_Reports].[dbo].[hub_DDJPFile] s
where exists (SELECT [ZGDCS]
FROM [HUB_DATA].[dbo].[SSCUSTP] d
where [ZGIDNO]=#a
and
s.DPACS=d.ZGDCS)
order by [DPVLDT] desc""", str(var))
rows = cursor.fetchall()
for i in range(a):
pub.sendMessage(i, rows[i].DPVLDT)
else:
self.test.SetLabel('False')
def afterOnbutton(self,e):
wx.CallAfter(self.Onbutton,e)
var = self.editquote1.GetValue()
if len(var)==9:
self.new = Childframe(None)
self.new.Show()
class Childframe(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel=wx.Panel(self)
self.windowSizer = wx.BoxSizer()
self.windowSizer.Add(self.panel, wx.ID_ANY, 13, wx.ALL | wx.EXPAND, 13)
pub.subscribe(self.amount, "amount")
for n in range(3):
pub.subscribe(self.row, n)
def amount(self, amount):
for n in range(amount.data):
[].append(wx.StaticText(self.panel, id=n, label='test', pos=(20, 30 * n)))
N = amount.data
return N
#I'm having problem here. I can't get N and I don't know how to SetLabel in the static text above
def row(self, row):
for n in range(N):
[].SetLabel(str(row.data))
if __name__=='__main__':
app=wx.App(False)
frame1=Childframe(None)
frame=Mainframe(None)
frame.Show()
app.MainLoop()
The problem is wrong timing between the Childframe creation and the sending of the message. When Mainframe send the message, the new frame does not exist yet and therefore the message is not received by anybody. Try splitting the events bound to the button, instead of:
self.button.Bind(wx.EVT_BUTTON, self.Onbutton and self.afterOnbutton)
try:
self.button.Bind(wx.EVT_BUTTON, self.afterOnbutton)
and modify the code in afterOnbutton such that the other function Onbutton is called after:
def afterOnbutton(self):
wx.CallAfter(self.Onbutton)
var = self.editquote1.GetValue()
if len(var)==9:
self.new = Childframe(None)
self.new.Show()
def Onbutton(self):
var = self.editquote1.GetValue()
if len(var)==9:
self.test.SetLabel('True')
... same ...
does anyone of you have an example how to make the following possible:
I have a listctrl that displays > 600 items. Now I need to search in these items for a text the user inputs and update the list to only show the items containing this string.
So let us say the list contains "Hello", "Hi" and "Morning". The list displays all three items. Now the user types "h" into the textctrl, and the listctrl is narrowed down to "Hello" and "Hi". If the user instead types "o", and the list becomes "Hello" and "Morning".
Is this possible? Or is there any other convenient way to find an item in a listctrl? The build in "find as you type" is only of real use if you do exactly know what you search for - and in my case this will not really be the case...
Thanks, Woodpicker
The wxPython demo has a pretty good "type-ahead" filter built into it. Looking at the source code to Main.py they do it the "manual way", loop and rebuild the list. They are using a treeview but the ideas are sound:
def OnSearch(self, event=None):
value = self.filter.GetValue()
if not value:
self.RecreateTree()
return
wx.BeginBusyCursor()
for category, items in _treeList:
self.searchItems[category] = []
for childItem in items:
if SearchDemo(childItem, value):
self.searchItems[category].append(childItem)
wx.EndBusyCursor()
self.RecreateTree()
I like the ObjectListView wrapper better than the straight wx.ListCtrl. It includes the ability to filter items in the control as a feature of the widget. You can read about it here: http://objectlistview.sourceforge.net/python/features.html#filtering and here's the main page for the control: http://objectlistview.sourceforge.net/python/
Here is an example of filtering an UltimateListCtrl.
I know this is 2 years later but I've found other examples on stackoverflow really, really helpful.
I'm new to python/wxpython but hopefully it will be useful.
starting from http://www.blog.pythonlibrary.org/2011/11/02/wxpython-an-intro-to-the-ultimatelistctrl/
import wx
from wx.lib.agw import ultimatelistctrl as ULC
class ULC_Panel(wx.Panel):
""""""
def __init__(self, parent, col_headers=None, list_data=None, options=None, dlg=None,
selected_list=None):
"""Constructor"""
wx.Panel.__init__(self, parent)
self.options = options
self.show_only_selected = False
self.filter_string = ""
hsizer = wx.BoxSizer(wx.HORIZONTAL)
okayButton = wx.Button(self, wx.ID_OK, "OK")
okayButton.SetToolTip(wx.ToolTip("Click to close this dialog and use the selections"))
self.Bind(wx.EVT_BUTTON, self.OnOkayCanButton, okayButton)
hsizer.Add(okayButton, 0, wx.ALL, 5)
canButton = wx.Button(self, wx.ID_CANCEL, "Cancel")
canButton.SetToolTip(wx.ToolTip("Click to close this dialog and cancel selections"))
self.Bind(wx.EVT_BUTTON, self.OnOkayCanButton, canButton)
hsizer.Add(canButton, 0, wx.ALL, 5)
cb_show_only = wx.CheckBox(self, -1, "Show only selected items?")
cb_show_only.SetValue(self.show_only_selected)
cb_show_only.SetToolTip(wx.ToolTip("Click to show only selected rows"))
self.Bind(wx.EVT_CHECKBOX, self.EvtShowOnly, cb_show_only)
hsizer.Add(cb_show_only, 0, wx.ALL, 5)
self.stext = wx.StaticText(self, -1, "Filter: ", style=wx.ALIGN_LEFT)
self.filtr = wx.TextCtrl(self, -1, "", style=wx.ALIGN_LEFT)
self.Bind(wx.EVT_TEXT, self.OnFiltr, self.filtr)
fsizer = wx.BoxSizer(wx.HORIZONTAL)
fsizer.Add(self.stext, 0, wx.ALL)
fsizer.Add(self.filtr, 1, wx.EXPAND)
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldfont.SetWeight(wx.BOLD)
boldfont.SetPointSize(12)
self.ultimateList = ULC.UltimateListCtrl(self, agwStyle = wx.LC_REPORT
| wx.LC_VRULES | ULC.ULC_HAS_VARIABLE_ROW_HEIGHT
| wx.LC_HRULES)
self.checkbox = [None] * len(list_data)
if selected_list != None:
self.selected = selected_list
else:
self.selected = [False] * len(list_data)
self.rows_max = len(list_data)
self.rows_current = -1
self.cols_max = len(col_headers)
self.cols_extra = 1
if options & ULC.ULC_MASK_CHECK:
self.cols_extra += 1
for i in xrange(self.cols_max+self.cols_extra):
info = ULC.UltimateListItem()
info._mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
info._image = []
info._format = 0
info._kind = 1
width = 150
if i >= self.cols_extra:
info._text = col_headers[i-self.cols_extra]
elif i == 0:
info._text = "Row"
width = 35
elif i == 1 and options & ULC.ULC_MASK_CHECK:
info._text = "Select"
width = 50
self.ultimateList.InsertColumnInfo(i, info)
self.ultimateList.SetColumnWidth(i, width)
self.list_data = list_data
pos = self.populate_table("")
if pos != None:
self.sz = self.ultimateList.GetItemRect(pos)
self.width = self.sz[2] + self.sz[3]
self.height = self.sz[1]
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(hsizer, 0, wx.EXPAND)
sizer.Add(fsizer, 0, wx.EXPAND)
sizer.Add(self.ultimateList, 1, flag=wx.EXPAND)
self.SetSizer(sizer)
sizer.Fit(self)
def EvtShowOnly(self, event):
cb = event.GetEventObject()
val = cb.GetValue()
self.show_only_selected = val
pos = self.populate_table(self.filter_string)
print "show_only_selected val= ", val
def EvtCheckBox(self, event):
cb = event.GetEventObject()
id = event.GetId()
val = cb.GetValue()
self.selected[id] = val
print "id, val= ", id, val
def OnOkayCanButton(self, event):
id = event.GetId()
dlg.EndModal(id)
def myGetNeedWH(self):
return (self.width, self.height)
def myGetSelectedState(self):
return self.selected
def populate_table(self, str_in):
busy = wx.BusyCursor()
if str_in:
str_low = str_in.lower()
if self.options & ULC.ULC_MASK_CHECK:
# if we have widgets in the row then we have to delete 1 row
# at a time (or else it leaves some of the widgets)
i = self.rows_current
#print "i, self.rows_max= ", i, self.rows_max
while i >= 0:
#print "i= ", i
self.ultimateList.DeleteItem(i)
i -= 1
else:
self.ultimateList.DeleteAllItems()
row = -1
for i in xrange(len(self.list_data)):
tlwr = self.list_data[i][0].lower()
if not str_in or tlwr.find(str_low) >= 0:
if self.show_only_selected and self.selected[i] == False:
continue
row += 1
for j in xrange(self.cols_max+self.cols_extra):
if j == 0:
pos = self.ultimateList.InsertStringItem(row, str(row))
elif j == 1 and self.options & ULC.ULC_MASK_CHECK:
self.checkbox[i] = wx.CheckBox(self.ultimateList, id= i)
self.checkbox[i].SetValue(self.selected[i])
self.checkbox[i].SetToolTip(wx.ToolTip("Click to select this row"))
self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.checkbox[i])
self.ultimateList.SetItemWindow(pos, col=1, wnd=self.checkbox[i], expand=False)
else:
self.ultimateList.SetStringItem(row, j, self.list_data[i][j-self.cols_extra])
self.rows_current = row
return row
def OnFiltr(self, event):
str1 = event.GetString()
id = event.GetId()
#print "got txt_tval str[%s]= %s" % (id, str1)
self.filter_string = str1
pos = self.populate_table(str1)
event.Skip()
return
########################################################################
class FilterListDiag(wx.Dialog):
def __init__(self, parent, id, title, headers=None, data_table=None, options=None, selected_list=None):
wx.Dialog.__init__(self, parent, id, title="", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
options_in = options
self.panel = ULC_Panel(self, col_headers=headers, list_data=data_table, options=options_in,
dlg=self, selected_list=selected_list)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.panel, 1, wx.EXPAND)
self.SetSizer(sizer)
(self.width, self.height) = self.panel.myGetNeedWH()
def myGetNeedWH(self):
return (self.width, self.height)
def myGetSelectedState(self):
return self.panel.myGetSelectedState()
class TestFrame(wx.Frame):
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="MvP UltimateListCtrl Demo", size=(850,600))
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = TestFrame()
col_headers = ['col0', 'col1', 'col2']
list_data = [
["Newsboys", "Go", "Rock"],
["Puffy", "Bring It!", "Pop"],
["Family Force 5", "III", "Pop"],
["Me2", "III", "Pop"],
["Duffy", "III", "Pop"],
["Fluffy", "III", "Pop"],
]
# sel_data passes in a list of which rows are already selected
sel_data = [
False,
False,
False,
False,
True,
False,
]
opt=ULC.ULC_MASK_CHECK # just reusing this to indicate I want a column of checkboxes.
dlg = FilterListDiag(frame, -1, "hi", headers=col_headers, data_table=list_data, options=opt, selected_list=sel_data)
(w, h) = dlg.myGetNeedWH()
print w,h
dlg.SetSizeWH(w, 300)
val = dlg.ShowModal()
selected = dlg.myGetSelectedState()
print "okay, can, val= ", wx.ID_OK, wx.ID_CANCEL, val
dlg.Destroy()
print 'selected=', selected
I have something different.
I created a dictionary and updated every time there is an entry in the list ctrl. Now, when I have a search box, which changes the list ctrl with each input from the keyboard and I clear and re-update my dictionary. This way, I always have the latest index for the values.
Code is as below:
class ViewUserDialog(wx.Dialog):
def __init__(self):
title = 'View Users Records'
super().__init__(parent=None, size=(750, 600), title=title)
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
self.list_sizer = wx.BoxSizer(wx.VERTICAL)
self.row_obj_dict = {}
.
.
.
self.list_ctrl_View_User.InsertColumn(0, "ID", width=150)
self.list_ctrl_View_User.InsertColumn(1, "User Name", width=150)
for index, users in original_user_frame.iterrows():
self.list_ctrl_View_User.InsertItem(indexes, user_id_value)
self.list_ctrl_View_User.SetItem(indexes, 1, users.NAME)
user_objects.append(users)
self.row_obj_dict[indexes] = users
indexes += 1
Now on searching, and selecting an item in lstctrl, you will still get your results:
def on_view(self, event):
selection = self.list_ctrl_View_User.GetFocusedItem()
self.list_ctrl_View_User.ClearAll()
if selection >= 0:
user = self.row_obj_dict[selection]
print(user)
self.list_ctrl_View_User.InsertColumn(0, "ID", width=150)
self.list_ctrl_View_User.InsertColumn(1, "User Name", width=150)
for index, users in original_user_frame.iterrows():
self.list_ctrl_View_User.InsertItem(indexes, user_id_value)
self.list_ctrl_View_User.SetItem(indexes, 1, users.NAME)
This will always give the currect selected item from the lstctrl and update the list with every input.
I've written a dialog in wxPtyhon with two comboboxes to which I've attached a custom validator, the purpose being to insure that, if a value is typed, it is an numeric string. Problem is, the validators aren't being invoked. What am I doing wrong?
import wx
# my custom validator class
class NumericObjectValidator(wx.Validator):
def __init__(self):
wx.Validator.__init__(self)
def Clone(self):
return NumericObjectValidator()
# Why isn't this method being called when the user types in the CB field?
def Validate(self, win):
cbCtrl = self.GetWindow()
text = cbCtrl.GetValue()
print 'control value=',text
return True
class SizeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, -1, 'Select Size', size=(200,135))
panel = wx.Panel(self, -1, size=self.GetClientSize())
sizes = map(str, range(3,21))
wx.StaticText(panel, -1, 'Rows:', pos=(10, 15))
self.rows = wx.ComboBox(panel, -1, value='8', choices=sizes,
style=wx.CB_DROPDOWN, pos=(80,15), validator=NumericObjectValidator())
wx.StaticText(panel, -1, 'Columns:', pos=(10,40))
self.cols = wx.ComboBox(panel, -1, choices=sizes, style=wx.CB_DROPDOWN,
pos=(80,40), value='8', validator=NumericObjectValidator())
cancel = wx.Button(panel,wx.ID_CANCEL,'Cancel', pos=(20,75))
wx.Button(panel,wx.ID_OK,'OK', pos=(100,75)).SetDefault()
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
def get_size(self):
r = int(self.rows.GetValue())
c = int(self.cols.GetValue())
return (c,r)
if __name__ == "__main__":
app = wx.App(0)
dlg = SizeDialog(None)
dlg.ShowModal()
dlg.Destroy()
This is a quirk in wxWidgets. If the parent is not a (subclass of) wx.Dialog, you must call the methods "TransferDataToWindow" and "TransferDataFromWindow" on the wx.Window manually. Here you are using wx.Panel as parent for the combo-boxes, therefore the data transfer is not automatically invoked.
I have no idea why, but copying the wxPython demo works okay.
import wx
import string
# my custom validator class
class NumericObjectValidator(wx.PyValidator):
def __init__(self):
wx.PyValidator.__init__(self)
self.Bind(wx.EVT_CHAR, self.OnChar)
def Clone(self):
return NumericObjectValidator()
def Validate(self, win):
tc = self.GetWindow()
val = tc.GetValue()
for x in val:
if x not in string.digits:
return False
return True
def OnChar(self, event):
key = event.GetKeyCode()
if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255:
event.Skip()
return
if chr(key) in string.digits:
event.Skip()
return
class SizeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, -1, 'Select Size', size=(200,135))
panel = wx.Panel(self, -1, size=self.GetClientSize())
sizes = map(str, range(3,21))
wx.StaticText(panel, -1, 'Rows:', pos=(10, 15))
self.rows = wx.ComboBox(panel, -1, value='8', choices=sizes,
style=wx.CB_DROPDOWN, pos=(80,15), validator=NumericObjectValidator())
wx.StaticText(panel, -1, 'Columns:', pos=(10,40))
self.cols = wx.ComboBox(panel, -1, style=wx.CB_DROPDOWN, validator=NumericObjectValidator())
cancel = wx.Button(panel,wx.ID_CANCEL,'Cancel', pos=(20,75))
wx.Button(panel,wx.ID_OK,'OK', pos=(100,75)).SetDefault()
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
def get_size(self):
r = int(self.rows.GetValue())
c = int(self.cols.GetValue())
return (c,r)
if __name__ == "__main__":
app = wx.App(0)
dlg = SizeDialog(None)
dlg.ShowModal()
dlg.Destroy()
Use PyValidator instead of Validator
the parent of your ComboBox and Button should be Dialog rather than Panel
The following version of your code shall work fine:
import wx
# my custom validator class
class NumericObjectValidator(wx.PyValidator):
def __init__(self):
wx.PyValidator.__init__(self)
def Clone(self):
return NumericObjectValidator()
# Why isn't this method being called when the user types in the CB
# field?
def Validate(self, win):
cbCtrl = self.GetWindow()
text = cbCtrl.GetValue()
print 'control value=', text
return True
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
class SizeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(
self, parent, -1, 'Select Size', size=(200, 135))
sizes = map(str, range(3, 21))
wx.StaticText(self, -1, 'Rows:', pos=(10, 15))
self.rows = wx.ComboBox(self, -1, value='8', choices=sizes,
style=wx.CB_DROPDOWN, pos=(80, 15), validator=NumericObjectValidator())
wx.StaticText(self, -1, 'Columns:', pos=(10, 40))
self.cols = wx.ComboBox(
self, -1, choices=sizes, style=wx.CB_DROPDOWN,
pos=(80, 40), value='8', validator=NumericObjectValidator())
cancel = wx.Button(self, wx.ID_CANCEL, 'Cancel', pos=(20, 75))
wx.Button(self, wx.ID_OK, 'OK', pos=(100, 75)).SetDefault()
def get_size(self):
r = int(self.rows.GetValue())
c = int(self.cols.GetValue())
return (c, r)
if __name__ == "__main__":
app = wx.App(0)
dlg = SizeDialog(None)
dlg.ShowModal()
dlg.Destroy()