So, I have two pieces of code. The first is the GUI class:
'''
Created on Mar 6, 2013
#author: Zach
'''
# -*- coding: utf-8 -*-
###########################################################################
## Python code generated with wxFormBuilder (version Sep 8 2010)
## http://www.wxformbuilder.org/
##
## PLEASE DO "NOT" EDIT THIS FILE!
###########################################################################
import wx
import wx.grid
from Books import *
###########################################################################
## Class MyFrame1
###########################################################################
class MyFrame1 ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 734,344 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
bSizer1 = wx.BoxSizer( wx.VERTICAL )
bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
self.patrons_table = wx.grid.Grid( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
# Grid
self.patrons_table.CreateGrid( 0, 7 )
self.patrons_table.EnableEditing( True )
self.patrons_table.EnableGridLines( True )
self.patrons_table.SetGridLineColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_APPWORKSPACE ) )
self.patrons_table.EnableDragGridSize( True )
self.patrons_table.SetMargins( 0, 0 )
# Columns
self.patrons_table.SetColSize( 0, 100 )
self.patrons_table.SetColSize( 1, 100 )
self.patrons_table.SetColSize( 2, 100 )
self.patrons_table.SetColSize( 3, 100 )
self.patrons_table.SetColSize( 4, 100 )
self.patrons_table.SetColSize( 5, 100 )
self.patrons_table.SetColSize( 6, 100 )
self.patrons_table.EnableDragColMove( True )
self.patrons_table.EnableDragColSize( True )
self.patrons_table.SetColLabelSize( 40 )
self.patrons_table.SetColLabelValue( 0, "ID" )
self.patrons_table.SetColLabelValue( 1, "Name" )
self.patrons_table.SetColLabelValue( 2, "Address" )
self.patrons_table.SetColLabelValue( 3, "Phone" )
self.patrons_table.SetColLabelValue( 4, "Email" )
self.patrons_table.SetColLabelValue( 5, "Fees/Day")
self.patrons_table.SetColLabelValue( 6, "Fees Owed" )
self.patrons_table.SetColLabelAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
# Rows
self.patrons_table.AutoSizeRows()
self.patrons_table.EnableDragRowSize( True )
self.patrons_table.SetRowLabelSize( 80 )
self.patrons_table.SetRowLabelAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
# Label Appearance
# Cell Defaults
self.patrons_table.SetDefaultCellAlignment( wx.ALIGN_LEFT, wx.ALIGN_TOP )
self.patrons_table.SetToolTipString( u"Table of patrons in the library" )
bSizer2.Add( self.patrons_table, 7, wx.EXPAND|wx.ALL|wx.ALIGN_BOTTOM, 5 )
bSizer6 = wx.BoxSizer( wx.VERTICAL )
self.m_panel1 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
bSizer6.Add( self.m_panel1, 1, wx.EXPAND |wx.ALL, 5 )
self.m_button1 = wx.Button( self, wx.ID_ANY, u"Add", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_button1.SetToolTipString( u"Adds a patron" )
bSizer6.Add( self.m_button1, 0, wx.ALL, 5 )
self.m_button2 = wx.Button( self, wx.ID_ANY, u"Remove", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_button2.SetToolTipString( u"Removes the selected patron" )
bSizer6.Add( self.m_button2, 0, wx.ALL, 5 )
self.m_button3 = wx.Button( self, wx.ID_ANY, u"Update", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_button3.SetToolTipString( u"Updates the database" )
bSizer6.Add( self.m_button3, 0, wx.ALL, 5 )
bSizer2.Add( bSizer6, 1, wx.EXPAND, 5 )
bSizer1.Add( bSizer2, 1, wx.EXPAND, 5 )
bSizer5 = wx.BoxSizer( wx.HORIZONTAL )
self.m_searchCtrl2 = wx.SearchCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 715,-1 ), 0 )
self.m_searchCtrl2.ShowSearchButton( True )
self.m_searchCtrl2.ShowCancelButton( False )
bSizer5.Add( self.m_searchCtrl2, 0, wx.ALL|wx.EXPAND, 5 )
bSizer1.Add( bSizer5, 0, wx.EXPAND, 5 )
self.SetSizer( bSizer1 )
self.Layout()
self.Centre( wx.BOTH )
# Connect Events
self.patrons_table.Bind( wx.grid.EVT_GRID_CELL_CHANGE, self.onGridChange )
self.patrons_table.Bind( wx.grid.EVT_GRID_SELECT_CELL, self.onLeftClick )
self.m_button1.Bind( wx.EVT_BUTTON, self.addpatron )
self.m_button2.Bind( wx.EVT_BUTTON, self.removepatron )
self.m_button3.Bind( wx.EVT_BUTTON, self.updatepatronsDatabase )
self.m_searchCtrl2.Bind( wx.EVT_SEARCHCTRL_SEARCH_BTN, self.searchpatrons )
self.m_searchCtrl2.Bind( wx.EVT_TEXT_ENTER, self.searchpatrons )
#Import the Database into the table
self.lib = Library()
self.db = Database()
for i in self.db.getPatrons():
print "hello"
self.lib.addPatron(Patron(i[0], i[1], i[2], i[3], i[4]))
self.lib.resetHistory()
for i in self.lib.patrons:
self.patrons_table.InsertRows()
self.patrons_table.SetCellValue(0,0,str(i["title"]))
self.patrons_table.SetCellValue(0,1,str(i["author"]))
self.patrons_table.SetCellValue(0,2,str(i["patron_id"]))
self.patrons_table.SetCellValue(0,3,str(i["location"]))
self.patrons_table.SetCellValue(0,4,str(i["publisher"]))
self.patrons_table.SetCellValue(0,5,str(i["genre"]))
self.patrons_table.SetCellValue(0,6,str(i["copy_right"]))
self.patrons_table.SetCellValue(0,7,str(i["subject"]))
self.patrons_table.SetCellValue(0,8,str(i["is_checked_out"]))
def __del__( self ):
pass
# Virtual event handlers, overide them in your derived class
#EVENTS
def onGridChange( self, event ):# Called if one of the cell values was changed
updated_patron = Patron(self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),0),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),1),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),2),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),3),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),4),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),5),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),6),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),7),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),8),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),9),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),10))
self.lib.editPatron(self.selected_patron, updated_patron)
def onLeftClick( self, event ): #Turns the selected row into a patron
self.selected_patron = Patron(self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),0),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),1),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),2),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),3),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),4),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),5),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),6),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),7),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),8),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),9),
self.patrons_table.GetCellValue(self.patrons_table.GetGridCursorRow(),10))
self.row = self.patrons_table.GetGridCursorRow()
self.column = self.patrons_table.GetGridCursorCol()
event.Skip()
def addpatron( self, event ):# adds a patron to the library and the table
self.patrons_table.InsertRows()
self.lib.addpatron(Patron())
event.Skip()
def removepatron( self, event ): #removes a patron from the table and the library
self.lib.removepatron(self.selected_patron)
self.patrons_table.DeleteRows()
print self.lib.patrons
event.Skip()
def updatepatronsDatabase( self, event ): #syncs the database with the library
self.db.mergeWithLibrary(self.lib)
def searchpatrons( self, event ):
value = self.m_searchCtrl2.GetValue()
i=0
while self.patrons_table.GetNumberRows()-1 >i:
if self.patrons_table.GetCellValue(i,2) == value:
self.patrons_table.SetGridCursor(i,2)
self.patrons_table.SelectRow(i)
self.patrons_table.Scroll(i,i)
break
i = i+1
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = MyFrame1(None)
frame.Show()
frame.Maximize()
app.MainLoop()
And the second part of the code is the engine behind the GUI class:
'''
Created on Mar 2, 2013
#author: Braden
This is a test file to try to make the GUI easier to program
It worked!
'''
import sqlite3 as sq
class Book(object):
r"""
This class defines what a book is. All books in the library are book objects
"""
def __init__(self, title = "n", author = "n", book_id = "n", location = "n", publisher = "n", genre = "n", copy_right = "n", subject = "n", is_checked_out = "n", price = "n", fee_increment = "n"):
r"""
This function sets the basic attributes of each book
"""
self.attributes = {"title":title,
"author":author,
"book_id":book_id,
"location":location,
"publisher":publisher,
"genre":genre,
"copy_right":copy_right,
"subject": subject,
"is_checked_out":is_checked_out,
"price": price,
"fee_increment":fee_increment
}
def __str__(self):
r"""
This returns a string representation of the book. This is what makes " print book" possible.
"""
return str(self.attributes)
def __getitem__(self,key):
r"""
This returns an attribute in the book. It makes "book['title']" work.
"""
return self.attributes[key]
def __setitem__(self,key,value):
r"""
This sets an attribute to a different value. It makes "book['title'] = 'My New Title' " Possilble ...I think.
"""
self.attributes[key] = value
def __cmp__(self,book):
if self.attributes == book.attributes:
return True
else:
return False
class Patron(object):
r"""
This is the Patron class. It is almost identical to the Book class just with different attributes.
"""
def __init__(self, patron_id, name, address, phone, email):
self.attributes = { "patron_id" : patron_id,
"name" : name,
"address" : address,
"phone" : phone,
"email" : email,
"fee_balance" : 0.00,
"fees_per_day" : 0.00,
"books_checked_out" : "",
"books_overdue" : ""}
def __str__(self):
return str(self.attributes)
def __getitem__(self,key):
return self.attributes[key]
def __setitem__(self,key,value):
self.attributes[key] = value
class Library(object):
r"""
This is the Library Class. It manipulates a database that has been loaded into the RAM.
"""
def __init__(self):
r"""
All this does is set the default values for the two Loaded Databases and the history.
"""
self.books = []
self.patrons= []
self.history = []
def addBook(self,book):
r"""
This appends a book object to the self.books list. Then it appends the action to the self.history list.
"""
self.books.append(book)
self.history.append(("book","add",book))
def removeBook(self, Book):
r"""
This removes a book object from the self.books list. Then it appends the action to the self.history list
"""
self.books.remove(Book)
self.history.append(("book","remove",Book))
def editBook(self, old_book, new_book):
original_id = old_book["book_id"]
r"""
This edits a book object in the self.books list. Then it appends the action to the self.history list.
"""
old_book["title"] = new_book["title"]
old_book["author"] = new_book["author"]
old_book["book_id"] = new_book["book_id"]
old_book["location"] = new_book["location"]
old_book["publisher"] = new_book["publisher"]
old_book["genre"] = new_book["genre"]
old_book["copy_right"] = new_book["copy_right"]
old_book["subject"] = new_book["subject"]
old_book["is_checked_out"] = new_book["is_checked_out"]
old_book["price"] = new_book["price"]
old_book["fee_increment"] = new_book["fee_increment"]
self.history.append(("book","edit",old_book,original_id))
def addPatron(self, patron):
r"""
This appends a new Patron object to the self.patrons list. It then appends the action to the self.history list.
"""
self.patrons.append(patron)
self.history.append(("patron","add",patron))
print "bonjour"
print self.patrons[0]
def removePatron(self,patron):
r"""
This Removes a patron object from the self.patrons list. It then appends the actino to the self.history list.
"""
self.patrons.remove(patron)
self.history.append(("patron","remove",patron))
def editPatron(self,patron,attribute,value):
r"""
This edits a patron object in the self.patrons list. It then appends the action to the self.history list.
"""
patron[attribute] = value
self.history.append(("patron","edit",patron))
def resetHistory(self):
r"""
This resets the self.history list
"""
self.history=[]
def getBook(self,book_id):
r"""
This finds a book based on its id
"""
for i in self.books:
if i["book_id"] == str(book_id):
return i
def __str__(self):
for i in self.books:
print i
for i in self.patrons:
print i
return "done"
class Database():
r"""
This is the Database Class. It modifies the library.db file.
"""
def __init__ (self):
r"""
This presets the self.database variable and the self.cursor variable
"""
self.database = sq.connect('library.db')
self.cursor = self.database.cursor()
def mergeWithLibrary(self,library):
r"""
This is my personal favorite. It updates the library.db file based off of the changes the Library object.
"""
for i in library.history:
print i
if i[0] == "book":
if i[1] == "add":
self.addBook(i[2])
elif i[1] == "remove":
self.deleteBook(i[2])
elif i[1] == "edit":
self.editBook(i[2],i[3])
elif i[0] == "patron":
if i[1] == "add":
self.addPatron(i[2])
elif i[1] == "remove":
self.deletePatron(i[2])
elif i[2] == "edit":
self.editPatron(i[2])
library.resetHistory()
def getBooks(self):
r"""
This returns all of the books in the library.db database
"""
self.cursor.execute("SELECT * FROM books")
return self.cursor.fetchall()
def getPatrons(self):
r"""
This returns all of the Patrons in the library.db database
"""
self.cursor.execute("SELECT * FROM patrons")
print self.cursor.fetchall()
print "called"
return [(0,0,0,0,0,0,0,0,0,0,0,0)]
def editBook(self,Book,id):
r"""
This updates a Book
"""
self.cursor.execute("UPDATE books SET title = '"+Book["title"]+"', author = '"+Book["author"]+"', book_id= '"+Book["book_id"]+"', location = '"+Book["location"]+"', publisher = '"+Book["publisher"]+"', genre = '"+Book["genre"]+"', copy_right = '"+Book["copy_right"]+"', subject = '"+Book["subject"]+"', is_checked_out = '"+Book["is_checked_out"]+"', price = '"+Book["price"]+"', fee_increment = '"+Book["fee_increment"]+"' WHERE book_id = '"+id+"'")
self.database.commit()
print Book["fee_increment"]
def editPatron(self,Patron):
r"""
This edits a Patron
"""
self.cursor.execute("UPDATE patrons SET name= '"+Patron["name"]+"', address = '"+Patron["address"]+"', phone = '"+str(Patron["phone"])+"', email = '"+Patron["email"]+"', fee_balance = '"+str(Patron["fee_balance"])+"', fees_per_day = '"+str(Patron["fees_per_day"])+"', books_checked_out = '"+Patron["books_checked_out"]+"', books_overdue = '" +Patron["books_overdue"]+"' WHERE id ="+str(Patron["patron_id"]))
self.database.commit()
def deleteBook(self,Book):
r"""
This deletes a book
"""
self.cursor.execute("DELETE FROM books WHERE book_id = "+"'"+Book["book_id"]+"'")
self.database.commit()
def deletePatron(self,Patron):
r"""
"""
self.cursor.execute("DELETE FROM patrons WHERE id = "+"'"+str(Patron["patron_id"])+"'")
self.database.commit()
def addBook(self,Book):
r"""
This adds a book
"""
data = [
Book["title"],
Book["author"],
Book["book_id"],
Book["location"],
Book["publisher"],
Book["genre"],
Book["copy_right"],
Book["subject"],
Book["is_checked_out"],
Book["price"],
Book["fee_increment"]
]
self.cursor.execute("INSERT INTO books VALUES(?,?,?,?,?,?,?,?,?,?,?)", data)
self.database.commit()
def addPatron(self,Patron):
r"""
This adds a Patron
"""
data = [Patron["patron_id"],
Patron["name"],
Patron["address"],
Patron["phone"],
Patron["email"],
Patron["fee_balance"],
Patron["fees_per_day"],
Patron["books_checked_out"],
Patron["books_overdue"]]
self.cursor.execute("INSERT INTO patrons VALUES(?,?,?,?,?,?,?,?,?)",data)
self.database.commit()
Every time I run my code, all my little print statement flags pop up in the console, but the window doesn't open and then python crashes. Commenting out the for statement where I import my database into the table lets the program run, but any calls to self.lib.addPatron will crash the program again. I see no problems with that function, am I just stupid?
It would have helped if you'd given us the traceback you were getting. When I run this code, I get the following:
sqlite3.OperationalError: no such table: patrons
File "c:\Users\mdriscoll\Desktop\brk.py", line 191, in <module>
frame = MyFrame1(None)
File "c:\Users\mdriscoll\Desktop\brk.py", line 115, in __init__
for i in self.db.getPatrons():
File "c:\Users\mdriscoll\Desktop\Books.py", line 184, in getPatrons
self.cursor.execute("SELECT * FROM patrons")
Since you state that you too are getting an issue on "self.lib.addPatron", then that means that this is likely the same issue. As the traceback indicates, the table does not exist. You need to create the table (even if they're empty) BEFORE you can add anything to do them. This article might help you get started: http://www.blog.pythonlibrary.org/2012/07/18/python-a-simple-step-by-step-sqlite-tutorial/
When debugging WXpython programs that crash before giving you any useful output, you can save the errors to a logfile by specifying a redirect when you start the app. So changing the line that starts the app to:
app = wx.pySimpleApp(redirect=True, filename="logfile.txt")
will save your errors to a logfile in the same folder as your script, and then you will have a better idea of what's going on.
Related
I use Timer for plotting and storing at same time. When i plot 2 values, no losing data from serial port(60 lines in minute, my device=1Hz). But when i try to plot more than 2 values, it corrupts the data(~40 lines in minute).
1.Should i try thread or queue instead of wx.Timer?
2.Why does wx.Timer corrupt my data? or what's the problem?
3.Should i use serial port func. inside wx.Timer??
Where am i doing wrong and what? I need your help. Any help would be appreciated.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import wx
import wxmplot
import serial
import serial.tools.list_ports
import numpy as np
is_wxPhoenix = 'phoenix' in wx.PlatformInfo
if is_wxPhoenix:
PyDeadObjectError = RuntimeError
else:
from wx._core import PyDeadObjectError
class myframe ( wx.Frame ): #Panel ###(reading, saving, plotting as a real-time from serial port)
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent )
self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
bSizer1 = wx.BoxSizer( wx.VERTICAL )
self.plotframe = None
self.toggleBtn17 = wx.ToggleButton( self, wx.ID_ANY, u"GRAPH", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer1.Add( self.toggleBtn17, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 )
self.toggleBtn171 = wx.ToggleButton( self, wx.ID_ANY, u"Store", wx.DefaultPosition, wx.DefaultSize, 0 )
self.toggleBtn171.Hide()
bSizer1.Add( self.toggleBtn171, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 )
self.toggleBtn4 = wx.ToggleButton( self, wx.ID_ANY, u"Save", wx.DefaultPosition, wx.DefaultSize, 0 )
self.toggleBtn4.Hide()
bSizer1.Add( self.toggleBtn4, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 )
self.timer1 = wx.Timer()
self.timer1.SetOwner( self, 1 )
self.timer2 = wx.Timer()
self.timer2.SetOwner( self, 2 )
self.timer2.Start( 1000 ) ### running when app begins
self.timer3 = wx.Timer()
self.timer3.SetOwner( self, 3 )
self.timer3.Start( 15000 ) #999 ### running when app begins
self.Centre( wx.BOTH )
self.SetSizer( bSizer1 )
# Connect Events
self.toggleBtn17.Bind( wx.EVT_TOGGLEBUTTON, self.plot_aio )
self.Bind( wx.EVT_TIMER, self.timer1_plot, id=1 )
self.toggleBtn171.Bind( wx.EVT_TOGGLEBUTTON, self.store_f )
self.Bind( wx.EVT_TIMER, self.timer2_store, id=2 )
self.toggleBtn4.Bind( wx.EVT_TOGGLEBUTTON, self.save_f )
self.Bind( wx.EVT_TIMER, self.timer3_save, id=3 )
self.x1 = np.array([]) # coming data from serial port should list. in wxmplot, numpy array is the best choice for appending data
self.y1 = np.array([])
self.y2 = np.array([])
self.y3 = np.array([])
self.store = []
self.store_tempr = []
self.ser = serial.Serial('COM9', 9600)
def ShowPlotFrame(self, do_raise=True, clear=True):
"make sure plot frame is enabled, and visible"
if self.plotframe is None:
self.plotframe = wxmplot.MultiPlotFrame(rows=3, cols=3, panelsize=(350, 275))
self.has_plot = False
try:
self.plotframe.Show()
except PyDeadObjectError:
self.plotframe = wxmplot.MultiPlotFrame(rows=3, cols=3, panelsize=(350, 275))
self.plotframe.Show()
if do_raise:
self.plotframe.Raise()
if clear:
self.plotframe.panel.clear()
#self.plotframe.reset_config()
def plot_aio( self, event ): ### plot button(timer 1)
if self.timer1.IsRunning():
self.timer1.Stop()
print("timer1 stopped")
else:
print("tgl_timer1 starting...")
self.ShowPlotFrame()
self.timer1.Start( 500 )
def store_f( self, event ): ### store in the numpy array button but both Timer activated and Button hidden(timer 2)
event.Skip()
#=======================================================================
# if self.timer2.IsRunning():
# self.timer2.Stop()
# print("saving stopped")
# else:
# print("saving_timer2 is starting...")
# self.timer2.Start( 1000 )
#=======================================================================
def save_f( self, event ): ### del the storing data button for not using more memory (both Timer activated and Button hidden)
event.Skip()
#=======================================================================
# if self.timer3.IsRunning():
# self.timer3.Stop()
# print("timer 3 stopped")
# else:
# print("tgl_timer 3 starting...")
# self.timer3.Start( 10000 ) #501
#=======================================================================
def timer1_plot( self, event ): ### PLOT STORED DATA (not button but entegrated with plot_aio func which is button)
for line in self.store_tempr:
data=line.split(b",")
if data[0] == b"$GNGGA":
tim2=data[1]
timm=float(tim2)
tim=timm+30000
hour = tim//10000
minute = (tim//100)%100
second = tim%100
zaman = hour*3600 + minute*60 + second
self.x1 = np.append(self.x1, zaman)
latitude=data[2]
lat=float(latitude)
lat1=int(lat/100)
lat2=(lat%100)/60
lati=lat1+lat2
self.y2 = np.append(self.y2, lati)
longitude=data[4]
lon=float(longitude)
lon1=int(lon/100)
lon2=(lon%100)/60
longi=lon1+lon2
self.y3 = np.append(self.y3, longi)
altitude=data[9]
self.y1 = np.append(self.y1, float(altitude))
self.ShowPlotFrame()
self.plotframe.plot(self.x1, self.y1, panel=(0, 0), labelfontsize=6)
self.plotframe.plot(self.x1, self.y3, panel=(0, 1), color='red', labelfontsize=6)
self.plotframe.plot(self.y1, self.x1, panel=(1, 0), color='black', labelfontsize=5)
self.plotframe.plot(self.y2, self.y3, panel=(1, 1), fullbox=False)
self.plotframe.plot(self.x1, self.y1, panel=(0, 2), labelfontsize=6)
self.plotframe.plot(self.x1, self.y3, panel=(2, 1), color='red', labelfontsize=6)
self.plotframe.plot(self.y1, self.x1, panel=(2, 0), color='black', labelfontsize=5)
self.plotframe.plot(self.y2, self.y3, panel=(2, 2), fullbox=False)
del self.store_tempr[:]
def timer2_store( self, event ): ### STORE and WRITE TO .TXT FILE AND DELETE FROM THE LIST (not button)
print( "storing and saving")
for line in self.ser:
self.store.append(line)
self.store_tempr.append(line)
def timer3_save( self, event ): ### DELETE STORED DATA IN THE LIST (not button)
with open("C:\\Users\\Desktop\\4n.txt","a") as f:
for line in self.store:
f.writelines(str(line)+ "\n")
del self.store[:]
if __name__ == "__main__":
app = wx.App(False)
frame = myframe(None)
frame.Show(True)
app.MainLoop()
I think you should not need to use Threads or Queues instead of wx.Timers. But, I also think you actually need only 1 wx.Timer that checks for and grabs data from the serial port (or other data source). I would suggest that the handler for the wx.Timer events (probably running at ~2Hz if you expect data at 1Hz) should do the following:
check for new data. if there is not new data, return immediately, waiting for next wx.Timer event.
if there is new data, parse and do the calculations based on that data right away and append it to the data arrays within that event handler. Just drop all the storing and later deleting of temporary data and have you self.x1, self.y1 etc up-to-date when the data-event-handler ends. All those del XXX in your code -- especially since one event handler deletes data created in another place - look like they could be a problem.
and then update the plots. If you believe the plotting will be slow, you could use a second timer event that looks at whether the length of self.x1 has changed and remake the plot(s). But, I believe you should not need to use a second timer, and can just update the plots in the data-event-handler.
For an example of how this might be done, see https://github.com/newville/wxmplot/blob/master/examples/stripchart.py
That uses just one wx.Timer that fetches new data and updates the plot. Note that it uses wxmplot.PlotPanel.update_line() which is much faster at updating an existing plot than redoing wxmplot.PlotPanel.plot() for each new data set.
The next_data() function in that example is a bit simpler and more deterministic than what you would need to do to read data from the serial port. But you're already doing that part and what you're doing doesn't look too hard or slow.
I have a problem when I do click on the listbox, In my program, I did click on the listbox will appear a video and explain about the video. I made a sample in the database are "name_link" which will appear in the listbox. expl1, expl2, expl3. Of each name_link have different information. However, that happens every time I click on one of these name_link, video that appears only Video3 and system never show the explain about video. When I click name_link video1 video2 emerging or always Video3. I am so stuck in this section.
this section during click :
tb4 = wx.StaticText(self, -1, label='explain', pos=(20, 145))
wx.TextCtrl(self, -1, pos=(80, 145), size=(220, 120))
self.opt = wx.ListBox(pan1, -1, pos=(10, 210), size=(480, 250), style= wx.TE_MULTILINE | wx.BORDER_SUNKEN)
def playFile(self, event):
self.player.Play()
def OnEnter(self, event):
self.opt.SetLabel(self.PatMatch())
def PatMatch(self):
con = sqlite3.connect('test.db')
with con:
cur = con.cursor()
for row in cur.execute("Select * From Video"):
klmt = self.inpt.GetValue()
if row[1] in klmt.lower():
self.opt.Append(row[2])
self.player.Load(row[4])
return self.Bind(wx.EVT_LISTBOX_DCLICK, self.playFile, self.op)
The data base like this :
id word name_link explain link
--- ------ ----------- --------- --------
1 python Video1 test C:\Users\Ihsan\Downloads\Video1.MP4
2 python Video2 test1 C:\Users\Ihsan\Downloads\Video2.MP4
3 python Video3 test2 C:\Users\Ihsan\Downloads\Video3.MP4
There are several issues:
You want bind wx.EVT_LISTBOX_DCLICK only once. When the event is fired, you want to read out which item (GetSelection) was selected when double-clicking. Use GetSelections for multiple selections.
Bad indentation: In the inner loop (self.player…) and with the return self.Bind… which should be in the innermost loop. As it is written now it will bind once to the last element. As written in point 1 this is not the way how it is done anyhow.
In Bind, self.op should be self.opt
See the wxPython demo on the download page to see how to use ListBox sensibly.
EDIT: Code sample added
import wx
import sqlite3
class play_model(object):
def __init__(self):
# all the sqlite init stuff goes here, it is mocked for the sake of the example
self.conn = sqlite3.connect(':memory:')
c = self.conn.cursor()
c.execute('CREATE TABLE video (word text, name_link text, explain text, link text)')
newrecs = (('python', 'Video1', 'test1', r'C:\video1.MP4'),
('python', 'Video2', 'test2', r'C:\video2.MP4'),
('notpython', 'Video3', 'test3', r'C:\video3.MP4'),
('python', 'Video4', 'test4', r'C:\video4.MP4'),
('python', 'Video5', 'test5', r'C:\video5.MP4'),
)
for tup in newrecs:
c.execute('INSERT INTO video VALUES (?, ?, ?, ?)', tup)
self.map_explain = {}
def get_videos(self, klmt):
# you want to get videos matching a parameter?
sqlstr = 'SELECT * FROM video WHERE video.word = (?)'
c = self.conn.cursor()
c.execute(sqlstr, (klmt.lower(),))
res = c.fetchall()
return res
class playframe(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
pnl = wx.Panel(self, -1)
self.explain = wx.StaticText(pnl, -1, 'Click in list to show explanation')
self.klmt = wx.TextCtrl(pnl, -1, '')
self.srch = wx.Button(pnl, -1, u'Search…')
self.vids = wx.ListBox(pnl, -1, style=wx.LB_MULTIPLE)
szmain = wx.BoxSizer(wx.VERTICAL)
szmain.Add(wx.StaticText(pnl, -1, 'Search for video category:'), 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.klmt, 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.srch, 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.vids, 1, wx.EXPAND|wx.ALL, 4)
szmain.Add(wx.StaticText(pnl, -1, 'Explanation for video'), 0, wx.EXPAND|wx.ALL, 4)
szmain.Add(self.explain, 0, wx.EXPAND|wx.ALL, 4)
pnl.SetSizer(szmain)
szmain.Fit(self)
class controller(object):
def __init__(self, app):
self.model = play_model()
self.search_results = []
# print self.model.get_videos('python')
self.frm = playframe(None, -1, 'test_playframe')
self.frm.Show()
self.frm.srch.Bind(wx.EVT_BUTTON, self.on_srch)
self.frm.vids.Bind(wx.EVT_LISTBOX, self.onvid_dblclick)
def onvid_dblclick(self, evt):
sels = evt.GetEventObject().GetSelections()
for idx in sels:
self.frm.explain.SetLabel(self.search_results[idx][2])
print 'play video:', idx, self.search_results[idx]
def on_srch(self, evt):
klmt = self.frm.klmt.GetValue()
# print klmt
res = self.model.get_videos(klmt)
if res:
self.search_results = res
self.frm.vids.Clear()
self.frm.vids.AppendItems([row[1] for row in self.search_results])
else:
parent = self.frm
wx.MessageDialog(parent,
'Not found in word category: {0}'.format(klmt),
'Category not found').ShowModal()
if __name__ == '__main__':
app = wx.App(redirect=False)
controller(app)
app.MainLoop()
I was try create some code like this.
def playVideo(self, evt, temp):
self.player.Load(evt.GetClientObject())
self.Play(True)
self.pjs.Clear()
self.pjs.AppendText(evt.GetClientObject())
Its work for the video, the video can play. but for the information from column database explain, its not show. i want the information from column explain print in tb4. #nepix32
I want to use a graph that is generated by DOT (pyDot in python) as the basis for an interactive Tree-structured GUI in which each of the nodes in the Tree could be widgets.
The tree will basically be a binary Morse Code tree which start at the top node and navigate down the tree to their desired letter and select it. The node they want to select should be highlightable, and the contents (letters) of should be able to be changed based on user input.
Basically I want the nodes to be turned into full scale objects with tunable parameters that change as the interface is used. Can anyone point me in the right direction in order to do this?
I started with the demo code at: http://wiki.wxpython.org/AnotherTutorial#wx.TreeCtrl. I've added the build_tree, _build_tree_helper and build_conn_dict methods. The key methods of interest from the dot_parser library are edge.get_source() and edge.get_destination() which are used to make the "connection" dictionary.
The dot graph is stored in the dot_data variable. Importantly, the dot graph must not loop; that is, it must be a spanning tree otherwise the _build_tree_helper method will loop infinitely (and it doesn't make sense in a TreeControl).
I also had to patch dot_parser according to https://github.com/nlhepler/pydot-py3/issues/1#issuecomment-15999052 to get it to work.
import wx
from dot_parser import parse_dot_data
class MyFrame(wx.Frame):
def __init__(self, parent, id, title, **kwargs):
self.parsed_dot = kwargs.pop("parsed_dot", None)
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(450, 350))
hbox = wx.BoxSizer(wx.HORIZONTAL)
vbox = wx.BoxSizer(wx.VERTICAL)
panel1 = wx.Panel(self, -1)
panel2 = wx.Panel(self, -1)
self.tree = wx.TreeCtrl(panel1, 1, wx.DefaultPosition, (-1,-1), wx.TR_HAS_BUTTONS | wx.TR_LINES_AT_ROOT )
self.build_tree(self.tree)
self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1)
self.display = wx.StaticText(panel2, -1, "",(10,10), style=wx.ALIGN_CENTRE)
vbox.Add(self.tree, 1, wx.EXPAND)
hbox.Add(panel1, 1, wx.EXPAND)
hbox.Add(panel2, 1, wx.EXPAND)
panel1.SetSizer(vbox)
self.SetSizer(hbox)
self.Centre()
def build_conn_dict(self):
conn_dict = {}
if(self.parsed_dot):
for edge in self.parsed_dot.get_edges():
conn_dict.setdefault(edge.get_source(), []).append(edge.get_destination())
return conn_dict
def build_tree(self, tree):
if(self.parsed_dot):
conn_dict = self.build_conn_dict()
outs = set(conn_dict.keys())
ins = reduce(lambda x, y: x | set(y), conn_dict.values(), set([]))
roots = list(outs - ins)
roots = dict([(root, tree.AddRoot(root)) for root in roots])
self._build_tree_helper(tree, conn_dict, roots)
def _build_tree_helper(self, tree, conn_dict = {}, roots = {}):
new_roots = {}
for root in roots:
if(conn_dict.has_key(root)):
for sub_root in conn_dict[root]:
new_roots[sub_root] = tree.AppendItem(roots[root], sub_root)
if(new_roots):
self._build_tree_helper(tree, conn_dict, new_roots)
def OnSelChanged(self, event):
item = event.GetItem()
self.display.SetLabel(self.tree.GetItemText(item))
child_text = self.tree.GetItemText(item)
parent_text = ""
try:
parent = self.tree.GetItemParent(item)
parent_text = self.tree.GetItemText(parent)
except wx._core.PyAssertionError:
pass
print "child: %s, parent: %s" % (child_text, parent_text)
class MyApp(wx.App):
def OnInit(self):
dot_data = \
'''
graph ""
{
label="(% (EXP (% (X) (% (X) (X)))) (EXP (SIN (X))))"
n039 ;
n039 [label="%"] ;
n039 -> n040 ;
n040 [label="EXP"] ;
n040 -> n041 ;
n041 [label="%"] ;
n041 -> n042 ;
n042 [label="X"] ;
n041 -> n043 ;
n043 [label="%"] ;
n043 -> n044 ;
n044 [label="X"] ;
n043 -> n045 ;
n045 [label="X"] ;
n039 -> n046 ;
n046 [label="EXP"] ;
n046 -> n047 ;
n047 [label="SIN"] ;
n047 -> n048 ;
n048 [label="X"] ;
}
'''
parsed_dot = parse_dot_data(dot_data)
frame = MyFrame(None, -1, "treectrl.py", parsed_dot = parsed_dot)
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
Ok, let's get straight to the point. I got 2 questions
1. Why self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:#000000" + FontSet) not working?
2. Why the custom lexer is not applied?
This is the source code
import wx, re, keyword
from wx import stc
class BaseLexer(object):
"""Defines simple interface for custom lexer objects"""
def __init__(self):
super(BaseLexer, self).__init__()
def StyleText(self, event):
raise NotImplementedError
class STC_LEX_VOTONUSA(BaseLexer):
# Define some style IDs
STC_VOTONUSA_DEFAULT = wx.NewId()
STC_VOTONUSA_VALUE = wx.NewId()
STC_VOTONUSA_OBJECT = wx.NewId()
STC_VOTONUSA_TYPE = wx.NewId()
STC_VOTONUSA_OPERATION = wx.NewId()
STC_VOTONUSA_COMMENT =wx.NewId()
STC_VOTONUSA_QUALIFIER = wx.NewId()
def __init__(self):
super(STC_LEX_VOTONUSA, self).__init__()
# Attributes
self.Comment = re.compile("""
//
""", re.X)
self.Types = re.compile("""
\\b
(void|integer|shortint|(short)?word|float|boolean|char|record|program|module)
\\b
""", re.I|re.X)
self.Operation = re.compile("""
(read|write)(line)?|if|main|case|while|use|return|exit|in_case|repeat_until
(\+|-|\*|/|%|\*\*|:|!|<|>)?=?|
(\{|\}|\(|\)|\[|\])
""", re.I|re.X)
self.Value = re.compile("""
[(`\d*)+\'\w*\']*|\'.*\'|[\+-]*\d*\.?\d*
""", re.I|re.X)
self.Qualifier = re.compile("""
interface|implementation
""", re.I|re.X)
self.Object = re.compile("""
s
""", re.I|re.X)
def GetLastWord(self, Line, CaretPos):
"""
Get the last word from a line
"""
LastWord = re.search(
"""
\s*
(
".*"| # String data type
\w*| # Any letter/number
(\+|-|\*|/|%|\*\*|:|!|<|>)?=?| # Assignment, Comparison & Mathematical
(\{|\}|\(|\)|\[|\]) # Brackets
)
\s*\Z
""",
Line[:CaretPos], re.VERBOSE)
return LastWord
def StyleText(self, event):
"""Handle the EVT_STC_STYLENEEDED event"""
Obj = event.GetEventObject()
# Get Last Correctly Styled
LastStyledPos = Obj.GetEndStyled()
# Get Styling Range
Line = Obj.LineFromPosition(LastStyledPos)
StartPos = Obj.PositionFromLine(Line)
EndPos = event.GetPosition()
# Walk the Range and Style Them
while StartPos < EndPos:
Obj.StartStyling(StartPos, 0x1f)
LastWord = self.GetLastWord(Line, CaretPos)
if self.Comment.search(LastWord):
# Set Comment Keyword style
Style = self.STC_VOTONUSA_COMMENT
elif self.Type.search(LastWord):
# Set Type Keyword style
Style = self.STC_VOTONUSA_TYPE
elif self.Operation.search(LastWord):
# Set Operation Keyword style
Style = self.STC_VOTONUSA_OPERATION
elif self.Value.search(LastWord):
# Set Value Keyword style
Style = self.STC_VOTONUSA_VALUE
elif self.Qualifier.search(LastWord):
# Set Qualifier Keyqord style
Style = self.STC_VOTONUSA_QUALIFIER
elif self.Object.search(LastWord):
# Set Object Keyword style
Style = self.STC_VOTONUSA_OBJECT
# Set the styling byte information for length of LastWord from
# current styling position (StartPos) with the given style.
Obj.SetStyling(len(LastWord), Style)
StartPos += len(LastWord)
class CustomSTC(stc.StyledTextCtrl):
def __init__(self, parent):
super(CustomSTC, self).__init__(parent)
# Attributes
self.custlex = None
Font = wx.Font(12, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
Face = Font.GetFaceName()
Size = Font.GetPointSize()
# Setup
kwlist = u" ".join(keyword.kwlist)
self.SetKeyWords(0, kwlist)
self.StyleClearAll()
self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA)
self.EnableLineNumbers()
FontSet = "face:%s, size:%d" % (Face, Size)
self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, FontSet)
# Set Default to Black
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:#000000" + FontSet)
# Set Comment to Pink
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT, "fore:#ff007f" + FontSet)
# Set Value to Green
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_VALUE, "fore:#00ff00" + FontSet)
# Set Object to Brown
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_OBJECT, "fore:#a52a2a" + FontSet)
# Set Type to Red
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_TYPE, "fore:#ff0000" + FontSet)
# Set Operation to Blue
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_OPERATION, "fore:#0000ff" + FontSet)
# Set Qualifier to Orange
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_QUALIFIER, "fore:#cc3232" + FontSet)
# Event Handlers
self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle)
def EnableLineNumbers(self, enable=True):
"""Enable/Disable line number margin"""
if enable:
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
self.SetMarginMask(1, 0)
self.SetMarginWidth(1, 25)
else:
self.SetMarginWidth(1, 0)
def OnStyle(self, event):
# Delegate to custom lexer object if one exists
if self.custlex:
self.custlex.StyleText(event)
else:
event.Skip()
def SetLexer(self, lexerid, lexer=None):
"""
Overrides StyledTextCtrl.SetLexer
Adds optional param to pass in custom container
lexer object.
"""
self.custlex = lexer
super(CustomSTC, self).SetLexer(lexerid)
class NUSAIPT(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, None, wx.ID_ANY, 'VOTO NUSA IPT')
self.TextArea = CustomSTC(self)
self.Show()
app = wx.App()
frame = NUSAIPT()
app.MainLoop()
If I change
self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA)
into
self.SetLexer(stc.STC_LEX_CPP)
and
self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT, "fore:#ff007f" + FontSet)
into
self.StyleSetSpec(stc.STC_C_COMMENT, "fore:#ff007f" + FontSet)
the comment highlighting worked. so the mistake should be in self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA) or STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT
Thanks in advance. Hope to see some answer soon.
Just stuck with same problem yesterday, here are my results. Hope it's not too late :)
First, i set lexer to this one, and internal lexer as you do:
self.SetLexer(stc.STC_LEX_CONTAINER)
self.custlex = SCT_LEX_ERLANG_IDNOISE(self)
Bind to event and OnStyle method are same.
And also i changed id of styles from wx.newId() to just numbers starting from 1.
Without it i didn't see any styling at all. Also stc.STC_STYLE_DEFAULT started to work too.
Full listing:
class SCT_LEX_ERLANG_IDNOISE(BaseLexer):
STC_ERLANG_IDNOISE_DEFAULT = 1
STC_ERLANG_IDNOISE_VARIABLE = 2
STC_ERLANG_IDNOISE_ATOM = 3
STC_ERLANG_IDNOISE_MODULE = 4
STC_ERLANG_IDNOISE_KEYWORD = 5
STC_ERLANG_IDNOISE_COMMENT = 6
STC_ERLANG_IDNOISE_MACROS = 7
STC_ERLANG_IDNOISE_NUMBER = 8
def __init__(self, control):
super(SCT_LEX_ERLANG_IDNOISE, self).__init__(control)
self.typeFormatDict = {}
self.typeFormatDict["other"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT
self.typeFormatDict["variable"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_VARIABLE
self.typeFormatDict["atom"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_ATOM
self.typeFormatDict["module"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MODULE
self.typeFormatDict["keyword"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_KEYWORD
self.typeFormatDict["comment"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_COMMENT
self.typeFormatDict["macros"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MACROS
self.typeFormatDict["number"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_NUMBER
def StyleText(self, event):
start = self.control.GetEndStyled()
end = event.GetPosition()
line = self.control.LineFromPosition(start)
start = self.control.PositionFromLine(line)
text = self.control.GetTextRange(start, end)
self.control.StartStyling(start, 0x1f)
lastEnd = 0
for type, start, end, value in getHighlightRules(text):
style = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT
if start > lastEnd:
self.control.SetStyling(start - lastEnd, style)
if type in self.typeFormatDict:
style = self.typeFormatDict[type]
self.control.SetStyling(len(value), style)
lastEnd = end
class CustomSTC(stc.StyledTextCtrl):
def __init__(self, parent):
super(CustomSTC, self).__init__(parent)
self.custlex = SCT_LEX_ERLANG_IDNOISE(self)
#self.SetKeyWords(0, kwlist)
self.SetLexer(stc.STC_LEX_CONTAINER)
self.EnableLineNumbers()
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, ColorSchema.formats["other"])
self.StyleClearAll()
self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, ColorSchema.lineFont)
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT, ColorSchema.formats["other"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_VARIABLE, ColorSchema.formats["variable"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_ATOM, ColorSchema.formats["atom"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MODULE, ColorSchema.formats["module"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_KEYWORD, ColorSchema.formats["keyword"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_COMMENT, ColorSchema.formats["comment"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MACROS, ColorSchema.formats["macros"])
self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_NUMBER, ColorSchema.formats["number"])
# Event Handlers
self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle)
def EnableLineNumbers(self, enable=True):
"""Enable/Disable line number margin"""
if enable:
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
self.SetMarginMask(1, 0)
self.SetMarginWidth(1, 35)
else:
self.SetMarginWidth(1, 0)
def OnStyle(self, event):
if self.custlex:
self.custlex.StyleText(event)
else:
event.Skip()
I'd actually like to start by admitting that I'm terrified to ask this question. That said, I have the following combination of classes:
A Dialog Class:
class formDialog(wx.Dialog):
def __init__(self, parent, id = -1, panel = None, title = _("Unnamed Dialog"),
modal = False, sizes = (400, -1)):
wx.Dialog.__init__(self, parent, id, _(title),
style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
if panel is not None:
self._panel = panel(self)
self._panel.SetSizeHints(*sizes)
ds = wx.GridBagSizer(self._panel._gap, self._panel._gap)
ds.Add(self._panel, (0, 0), (1, 1), wx.EXPAND | wx.ALL, self._panel._gap)
ds.Add(wx.StaticLine(self), (1, 0), (1, 1), wx.EXPAND | wx.RIGHT | wx.LEFT, self._panel._gap)
self.bs = self.CreateButtonSizer(self._panel._form['Buttons'])
ds.Add(self.bs, (2, 0), (1, 1), wx.ALIGN_RIGHT | wx.ALL, self._panel._gap)
ds.AddGrowableCol(0)
ds.AddGrowableRow(0)
self.SetSizerAndFit(ds)
self.Center()
self.Bind(wx.EVT_BUTTON, self._panel.onOk, id = wx.ID_OK)
self.Bind(wx.EVT_BUTTON, self._panel.onClose, id = wx.ID_CANCEL)
self.Bind(wx.EVT_CLOSE, self._panel.onClose)
if modal:
self.ShowModal()
else:
self.Show()
A Form Class:
class Form(wx.Panel):
reqFields = [
('Defaults', {}),
('Disabled', [])
]
def __init__(self, parent = None, id = -1, gap = 2, sizes = (-1, -1)):
wx.Panel.__init__(self, parent, id)
self.SetSizeHints(*sizes)
self._gap = gap
self.itemMap = {}
if hasattr(self, '_form'):
# There are a number of fields which need to exist in the form
# dictionary. Set them to defaults if they don't exist already.
for k, d in self.reqFields:
if not self._form.has_key(k):
self._form[k] = d
self._build()
def _build(self):
"""
The Build Method automates sizer creation and element placement by parsing
a properly constructed object.
"""
# The Main Sizer for the Panel.
panelSizer = wx.GridBagSizer(self._gap, self._gap)
# Parts is an Ordered Dictionary of regions for the form.
for group, (key, data) in enumerate(self._form['Parts'].iteritems()):
flags, sep, display = key.rpartition('-') ##UnusedVariable
# HR signifies a Horizontal Rule for spacing / layout. No Data Field.
if display == 'HR':
element = wx.StaticLine(self)
style = wx.EXPAND
# Any other value contains elements that need to be placed.
else:
element = wx.Panel(self, -1)
# The Row Sizer
rowSizer = wx.GridBagSizer(self._gap, self._gap)
for row, field in enumerate(data):
for col, item in enumerate(field):
style = wx.EXPAND | wx.ALL
pieces = item.split('-')
# b for Buttons
if pieces[0] == 'b':
control = wx._controls.Button(element, -1, pieces[1])
# custom items - Retrieve from the _form object
if pieces[0] == 'custom':
control = self._form[pieces[1]](element)
# The row in the Grid needs to resize for Lists.
panelSizer.AddGrowableRow(group)
# Now the Row has to grow with the List as well.
rowSizer.AddGrowableRow(row)
# custom2 - Same as custom, but does not expand
if pieces[0] == 'custom2':
control = self._form[pieces[1]](element)
style = wx.ALL
# c for CheckBox
if pieces[0] == 'c':
control = wx.CheckBox(element, label = _(pieces[2]), name = pieces[1])
control.SetValue(int(self._form['Defaults'].get(pieces[1], 0)))
# d for Directory Picker
if pieces[0] == 'd':
control = wx.DirPickerCtrl(element, name = pieces[1])
control.GetTextCtrl().SetEditable(False)
control.GetTextCtrl().SetName(pieces[1])
control.GetTextCtrl().SetValue(self._form['Defaults'].get(pieces[1], ''))
# f for File Browser
if pieces[0] == 'f':
control = wx.FilePickerCtrl(element, name = pieces[1], wildcard = pieces[2])
control.GetTextCtrl().SetEditable(False)
control.GetTextCtrl().SetValue(self._form['Defaults'].get(pieces[1], ''))
# f2 for Save File
if pieces[0] == 'f2':
control = wx.FilePickerCtrl(element, name = pieces[1],
style = wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT | wx.FLP_USE_TEXTCTRL,
wildcard = pieces[2])
control.GetTextCtrl().SetEditable(False)
# h for Horizontal Rule - layout helper.
if pieces[0] == 'h':
control = wx.StaticLine(element)
style = wx.EXPAND
# l for Label (StaticText)
if pieces[0] == 'l':
control = wx.StaticText(element, label = _(pieces[1]))
# Labels do not expand - override default style.
style = wx.ALL | wx.ALIGN_CENTER_VERTICAL
# p for Password (TextCtrl with Style)
if pieces[0] == 'p':
control = wx.TextCtrl(element, name = pieces[1], style = wx.TE_PASSWORD)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# s for ComboBox (Select)
if pieces[0] == 's':
control = wx.ComboBox(element, name = pieces[1],
choices = self._form['Options'].get(pieces[1], []),
style = wx.CB_READONLY)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# s2 for Spin Control
if pieces[0] == 's2':
control = wx.SpinCtrl(element, name = pieces[1], size = (55, -1),
min = int(pieces[2]), max = int(pieces[3]))
control.SetValue(int(self._form['Defaults'].get(pieces[1], 1)))
# Spin Ctrl's do not expand.
style = wx.ALL
# t for TextCtrl
if pieces[0] == 't':
control = wx.TextCtrl(element, name = pieces[1])
try:
control.SetValidator(self._form['Validators'][pieces[1]])
except KeyError: pass # No Validator Specified.
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# tr for Readonly TextCtrl
if pieces[0] == 'tr':
control = wx.TextCtrl(element, name = pieces[1], style = wx.TE_READONLY)
control.SetValue(self._form['Defaults'].get(pieces[1], ''))
# Check for elements disabled by default. Store reference to
# Element in itemMap for reference by other objects later.
if len(pieces) > 1:
if pieces[1] in self._form['Disabled']:
control.Enable(False)
self.itemMap[pieces[1]] = control
# Place the control in the row.
rowSizer.Add(control, (row, col), (1, 1), style, self._gap)
if style == wx.EXPAND | wx.ALL:
rowSizer.AddGrowableCol(col)
if 'NC' not in flags:
sb = wx.StaticBox(element, -1, _(display))
sz = wx.StaticBoxSizer(sb, wx.VERTICAL)
sz.Add(rowSizer, 1, flag = wx.EXPAND)
element.SetSizerAndFit(sz)
else:
element.SetSizerAndFit(rowSizer)
panelSizer.Add(element, (group, 0), (1, 1), wx.EXPAND | wx.ALL, self._gap)
panelSizer.AddGrowableCol(0)
self.SetSizerAndFit(panelSizer)
def getDescendants(self, elem, list):
children = elem.GetChildren()
list.extend(children)
for child in children:
self.getDescendants(child, list)
def getFields(self):
fields = []
self.getDescendants(self, fields)
# This removes children we can't retrieve values from. This should result
# in a list that only contains form fields, removing all container elements.
fields = filter(lambda x: hasattr(x, 'GetValue'), fields)
return fields
def onOk(self, evt):
self.onClose(evt)
def onClose(self, evt):
self.GetParent().Destroy()
The Form is meant to be used by subclassing like so:
class createQueue(Form):
def __init__(self, parent):
self._form = {
'Parts' : OrderedDict([
('Queue Name', [
('t-Queue Name',)
])
]),
'Buttons' : wx.OK | wx.CANCEL
}
Form.__init__(self, parent)
class generalSettings(Form):
def __init__(self, parent):
self._form = {
'Parts': OrderedDict([
('Log Settings', [
('l-Remove log messages older than: ', 's2-interval-1-10', 's-unit')
]),
('Folder Settings', [
('l-Spool Folder Location:', 'd-dir'),
('l-Temp Folder Location:', 'd-temp')
]),
('Email Notifications', [
('l-Alert Email To:', 't-alert_to'),
('l-Alert Email From:', 't-alert_from'),
('l-Status Email From:', 't-status_from'),
('l-Alert Email Server:', 't-alert_host'),
('l-Login:', 't-alert_login'),
('l-Password:', 'p-alert_password')
]),
('Admin User', [
('c-req_admin-Require Admin Rights to make changes.',)
]),
('Miscellaneous', [
('l-Print Worker Tasks:', 's2-printtasks-1-256', 'l-Job Drag Options:', 's-jobdrop')
])
]),
'Options': {
'unit': ['Hours', 'Days', 'Months'],
'jobdrop': ['Move Job to Queue', 'Copy Job to Queue']
},
'Buttons': wx.OK | wx.CANCEL
}
Form.__init__(self, parent)
These might be used like so:
formDialog(parent, panel = createQueue, title = 'Create a Queue', sizes = (200, -1))
formDialog(parent, panel = generalSettings, title = "General Settings")
Whew, that's a ton, and thanks to anyone who makes it down this far. The idea is that I want something that takes care of the monotonous parts of layout in wxPython. I am designing a user interface that will need to create 100's of different Dialogs and Forms. I wanted something that would allow me to generate the forms dynamically from a structured object.
I'd like to hear other developers' thoughts on this kind of approach. The closest I've seen to something similar is Drupal's Form API. I feel like it is viable for these reasons:
Easily rearrange fields.
No need to create / manage Sizers manually.
Compound / complex forms can be created easily.
Display helper elements (StaticBoxSizers, Static Lines) are easily added.
I am concerned that it is an undesirable approach for these reasons:
Long _build() function body in the Form Class.
May not be clear to other developers at first glance.
Uses Structured Strings to define fields.
There may be a better way.
Any thoughts, constructive, destructive, or otherwise will all be appreciated. Thanks!
You should also try wxFormDesigner or XRCed.
Since you're using wx, you should study wxglade. It's a graphical GUI builder which you use to build your GUI and it generates a .wxg file with the layout, and you can load that into your script.
The file is actually just xml, so you can programatically generate it and dynamically load different GUIs from it. Maybe that helps.