Passing values between forms with npyscreen - python-curses

I am trying to create a simple npyscreen curses application in python that requests user input on one screen, and then verifies it for the user on another screen.
Mostly, this is an effort to understand how values are stored and retrieved from within npyscreen. I am sure I am missing something simple, but I have been unable to find (or understand?) the answer within the documentation.
Sample code below which will not pass the value properly:
#!/usr/bin/env python3.5
import npyscreen as np
class EmployeeForm(np.Form):
def afterEditing(self):
self.parentApp.switchForm('CONFIRMFM')
def create(self):
self.myName = self.add(np.TitleText, name='Name')
self.myDepartment = self.add(np.TitleSelectOne, scroll_exit=True, max_height=3, name='Department', values = ['Department 1', 'Department 2', 'Department 3'])
self.myDate = self.add(np.TitleDateCombo, name='Date Employed')
class EmployeeConfirmForm(np.Form):
def afterEditing(self):
self.parentApp.setNextForm(None)
def create(self):
self.value = None
self.wgName = self.add(np.TitleText, name = "Name:",)
self.wgDept = self.add(np.TitleText, name = "Dept:")
self.wgEmp = self.add(np.TitleText, name = "Employed:")
def beforeEditing(self):
if self.value:
self.name = "Is this correct?"
self.wgName.value = self.myName.value
self.wgDept.value = self.myDepartment.value
self.wgEmp.value = self.myDate.value
def on_cancel(self):
self.parentApp.switchFormPrevious()
class myApp(np.NPSAppManaged):
def onStart(self):
self.addForm('MAIN', EmployeeForm, name='Employee Entry')
self.addForm('CONFIRMFM', EmployeeConfirmForm, name='Employee Confirmation')
if __name__ == '__main__':
TestApp = myApp().run()

I haven't found a straightforward way to directly pass variables between forms. However, you can work around this by using variables that have a global scope among forms.
The NPSAppManaged app class runs completely encapsulated, so if you try to declare global variables at the top of the Python file, the forms within will not have access to them. Instead, declare the variables inside the NPSAppManaged app class before the onStart method as shown below.
class myApp(np.NPSAppManaged):
# declare variables here that have global scope to all your forms
myName, myDepartment, myDate = None, None, None
def onStart(self):
self.addForm('MAIN', EmployeeForm, name='Employee Entry')
self.addForm('CONFIRMFM', EmployeeConfirmForm, name='Employee Confirmation')
if __name__ == '__main__':
TestApp = myApp().run()
You can then access these variables using self.parentApp.[variable name] as follows:
class EmployeeConfirmForm(np.Form):
def afterEditing(self):
self.parentApp.setNextForm(None)
def create(self):
self.add(np.FixedText, value = "Is this correct?")
self.wgName = self.add(np.TitleText, name = "Name:", value = self.parentApp.myName)
self.wgDept = self.add(np.TitleText, name = "Dept:", value = self.parentApp.myDepartment)
self.wgEmp = self.add(np.TitleText, name = "Employed:", value = self.parentApp.myDate)
def on_cancel(self):
self.parentApp.switchFormPrevious()
Note: you don't need to have a separate beforeEditing method since the values will be loaded directly into EmployeeConfirmForm during the create method from the app class global variables.

One way to do this is to use getForm before switchForm to pass the values. The following works for me using python2 to pass the name. Passing other values should be similar. I'm very much learning this myself but please comment if you have further questions.
import npyscreen as np
class EmployeeForm(np.ActionForm):
def create(self):
self.myName = self.add(np.TitleText, name='Name')
self.myDepartment = self.add(np.TitleSelectOne, scroll_exit=True, max_height=3, name='Department', values = ['Department 1', 'Department 2', 'Department 3'])
self.myDate = self.add(np.TitleDateCombo, name='Date Employed')
def afterEditing(self):
self.parentApp.getForm('CONFIRMFM').wgName.value = self.myName.value
self.parentApp.switchForm('CONFIRMFM')
class EmployeeConfirmForm(np.Form):
def afterEditing(self):
self.parentApp.setNextForm(None)
def create(self):
self.value = None
self.wgName = self.add(np.TitleFixedText, name = "Name:",)
self.wgDept = self.add(np.TitleText, name = "Dept:")
self.wgEmp = self.add(np.TitleText, name = "Employed:")
def on_cancel(self):
self.parentApp.switchFormPrevious()
class myApp(np.NPSAppManaged):
def onStart(self):
self.addForm('MAIN', EmployeeForm, name='Employee Entry')
self.addForm('CONFIRMFM', EmployeeConfirmForm, name='Employee Confirmation')
if __name__ == '__main__':
TestApp = myApp().run()

Assign values (including the value returned by addForm) to self.whatever.
So if you do
self.myAppValue = 2
self.mainForm = self.addForm('MAIN', MainForm, ...)
self.form2 = self.addForm('FORM2', Form2, ...)
you can use these in any form
self.parentApp.myAppValue
self.parentApp.mainForm.main_form_widget.main_form_value
And in myApp() you can do
self.mainForm.main_form_widget.main_form_value

I had the same problems during development as the documentation is not very detailed about passing values between forms.
I have added a few modification, the first one was changing Form to ActionForm in the confirmation form to have access to ok and cancel buttons. Secondly, the values on the confirmation form should not be editable as this is a confirmation. You can press cancel to edit on the previous form. The last one was passing the first element of the department list rather than the value - it would be just a list index. I hope it helps a little.
#!/usr/bin/env python3.5
import npyscreen as np
class EmployeeForm(np.Form):
def create(self):
self.myName = self.add(np.TitleText, name='Name')
self.myDepartment = self.add(np.TitleSelectOne, scroll_exit=True, max_height=3, name='Department', values = ['Department 1', 'Department 2', 'Department 3'])
self.myDate = self.add(np.TitleDateCombo, name='Date Employed')
def afterEditing(self):
self.parentApp.switchForm('CONFIRMFM')
self.parentApp.getForm('CONFIRMFM').wgName.value = self.myName.value
self.parentApp.getForm('CONFIRMFM').wgDept.value = self.myDepartment.values[0]
self.parentApp.getForm('CONFIRMFM').wgEmp.value = self.myDate.value
class EmployeeConfirmForm(np.ActionForm):
def create(self):
self.add(np.FixedText, value="Is this correct?", editable=False)
self.wgName = self.add(np.TitleText, name = "Name:", editable=False)
self.wgDept = self.add(np.TitleText, name = "Dept:", editable=False)
self.wgEmp = self.add(np.TitleText, name = "Employed:", editable=False)
def on_ok(self):
self.parentApp.setNextForm(None)
def on_cancel(self):
self.parentApp.switchFormPrevious()
class myApp(np.NPSAppManaged):
def onStart(self):
self.addForm('MAIN', EmployeeForm, name='Employee Entry')
self.addForm('CONFIRMFM', EmployeeConfirmForm, name='Employee Confirmation')
if __name__ == '__main__':
TestApp = myApp().run()

Related

How to create an object attribute without writing to database -- peewee -- python

Maybe i have an understanding problem. I try to make 2 tabeles in one database. But additionaly i need to have some temporary values in one class that i doen´t want to write to the database.
I try to switch to peewee and read the dokumentation but i find no solution at my own.
without peewee i would make an init method where i write my attributes. But where did i have to write them now?
from peewee import *
import datetime
db = SqliteDatabase('test.db', pragmas={'foreign_keys': 1})
class BaseModel(Model):
class Meta:
database = db
class Sensor(BaseModel):
id = IntegerField(primary_key=True)
sort = IntegerField()
name = TextField()
#def __init__(self):
#self.sometemporaryvariable = "blabla"
def meineparameter(self, hui):
self.hui = hui
print(self.hui)
class Sensor_measure(BaseModel):
id = ForeignKeyField(Sensor, backref="sensorvalues")
timestamp = DateTimeField(default=datetime.datetime.now)
value = FloatField()
class Meta:
primary_key = CompositeKey("id", "timestamp")
db.connect()
db.create_tables([Sensor_measure, Sensor])
sensor1 = Sensor.create(id=2, sort=20, name="Sensor2")
#sensor1.sometemporaryvariable = "not so important to write to the database"
sensor1.save()
Remember to call super() whenever overriding a method in a subclass:
class Sensor(BaseModel):
id = IntegerField(primary_key=True)
sort = IntegerField()
name = TextField()
def __init__(self, **kwargs):
self.sometemporaryvariable = "blabla"
super().__init__(**kwargs)

Returning a dictionary from a file

I have have written this code defining a class
class OrderRecord:
"""Defines an OrderRecord class, suitable for use in keeping track of order records"""
import tools2
def __init__(self, string):
"""Creates a new OrderRecord object"""
string = string.split(',')
self.date = string[0]
self.location = string[1]
self.name = string[2]
self.colour = string[3]
self.order_num = string[4]
self.cost = 0
def cost_of_order(self):
"""Creates a list of the name and adds up the cost of each letter"""
letter = list(self.name)
for let in letter:
self.cost = self.cost + self.tools2.letter_price(let, self.colour)
return self.cost
def __str__(self):
"""Calls the cost_of_order function and returns the split string in the required format"""
self.cost = self.cost_of_order()
return("Date: {0}\nLocation: {1}\nName: {2}\nColour: \
{3}\nOrder Num: {4}\nCost: {5:.2f}".format(self.date, self.location, \
self.name, self.colour, self.order_num, self.cost))
Now I need to write a function that reads a file containing the following:
20130902,Te Rakipaewhenua,Vinas,parauri,8638
20130909,Te Papaioea,McClary,kikorangi,11643
20131215,Kapiti,Labrie,kikorangi,65291
20141106,Waihopai,Labrie,ma,57910
and returns a dictionary that has the location as the key and lists of OrderRecords as the values.
I know this isn't too hard of a task but I have been stuck on this for awhile because I can't get my head around what to do for it.
Any help would be appreciated.
Maybe something like this. It is not the solution but it has what you need with some modifications.
import collections
dct_result = collections.defaultdict(list)
for line in open('file_path'):
fields = line.split(',')
# index 1 being the second column
dct_result[field(1)].append(OrderRecord( some args ))

Overriding validation for Django for base64 string for model.imagefield

I am using Angular and Bootstrap to serve my forms. If a user uploads an image, Angular serves it in the "data:" format, but Django is looking for a file type. I have fixed this issue by overriding both perform_authentication (To modify the image to a file) and perform_create (to inject my user_id). Is there a better way to override?
I'd rather not override my view. I'd rather override the way Django validates ImageFields. What I want to do is check if the passed value is a 64-bit string, if it is, modify it to a file type, then validate the ImageField. The below code works as is, I just don't feel is optimal.
Here is my view:
class UserCredentialList(generics.ListCreateAPIView):
permission_classes = (IsCredentialOwnerOrAdmin,)
serializer_class = CredentialSerializer
"""
This view should return a list of all the purchases
for the currently authenticated user.
"""
def get_queryset(self):
"""
This view should return a list of all models by
the maker passed in the URL
"""
user = self.request.user
return Credential.objects.filter(member=user)
def perform_create(self, serializer):
serializer.save(member_id=self.request.user.id)
def perform_authentication(self, request):
if request.method == 'POST':
data = request.data.pop('document_image', None)
from django.core.files.base import ContentFile
import base64
import six
import uuid
# Check if this is a base64 string
if isinstance(data, six.string_types):
# Check if the base64 string is in the "data:" format
if 'data:' in data and ';base64,' in data:
# Break out the header from the base64 content
header, data = data.split(';base64,')
# Try to decode the file. Return validation error if it fails.
try:
decoded_file = base64.b64decode(data)
except TypeError:
self.fail('invalid_image')
# Generate file name:
file_name = str(uuid.uuid4())[:12] # 12 characters are more than enough.
# Get the file name extension:
import imghdr
file_extension = imghdr.what(file_name, decoded_file)
file_extension = "jpg" if file_extension == "jpeg" else file_extension
complete_file_name = "%s.%s" % (file_name, file_extension,)
data = ContentFile(decoded_file, name=complete_file_name)
request.data['document_image'] = data
request.user
And here is my serializer:
class CredentialSerializer(serializers.ModelSerializer):
class Meta:
model = Credential
fields = (
'id',
'credential_type',
'credential_number',
'date_received',
'is_verified',
'date_verified',
'document_image',
)
And here is my model:
class Credential(models.Model):
"""Used to store various credentials for member validation."""
document_image = models.ImageField(
upload_to=get_upload_path(instance="instance",
filename="filename.ext",
path='images/credentials/'))
PASSENGER = 'P'
OWNER = 'O'
CAPTAIN = 'C'
CREDENTIAL_CHOICES = (
(PASSENGER, 'Passenger'),
(OWNER, 'Owner'),
(CAPTAIN, 'Captain'),
)
credential_type = models.CharField(max_length=1,
choices=CREDENTIAL_CHOICES,
default=PASSENGER)
credential_number = models.CharField(max_length=255)
date_received = models.DateTimeField(auto_now_add=True)
is_verified = models.BooleanField(default=False)
date_verified = models.DateTimeField(blank=True, null=True)
member = models.ForeignKey(settings.AUTH_USER_MODEL,
related_name='credentials')
I used the below link to help me, now I just want to figure out how override the proper method
Django REST Framework upload image: "The submitted data was not a file"
Well I've made one change since making: I have moved this function to my serializer and instead I now override the method: is_valid and that works as well. At least it's not in my view anymore.

GetSelection ComboBox

I can't figure out what else I need for the combobox to select
from my list as I type. Eventually I'm going to add SetInsertionPoint.
But for now, my item selected is allways -1
self.filter = wx.ComboBox(self, wx.ID_ANY, choices = '', style=wx.CB_DROPDOWN)
def OnTextChanged(self, event):
sel = self.filter.GetSelection()
print 'OnItemSelected %s' % sel
This other SO answer has a custom control that might work for you:
Auto-Completion In wxPython wxComboBox
You may also get ideas from this wxPython wiki article
http://wiki.wxpython.org/TextCtrlAutoComplete
I also noticed that the masked combo box may have this feature itself, according to the docs: http://www.wxpython.org/docs/api/wx.lib.masked.combobox-module.html which says the following
BaseMaskedComboBox - Base class for generic masked edit comboboxes; allows auto-complete of values.
To use the GetSelection() alone, you need to set the ComboBox to read only. It's a nice way to
query by hitting one charactor. Using SetInsertionPoint and SetMark keeps the curser to the next string of your query. I used the example Mike suggested •Auto-Completion In wxPython wxComboBox
and modified my code to used these instances. Since I always use boxsizers and open functions I needed to do away with the wx.EVT_TEXT event. Here's how it works:
## copy/paste to text file
'''
73,"N WASHINGTON ST"
95,"BRIAR CREEK RD"
97,"N INDEPENDENCE AVE"
09,"N ADAMS ST"
13,"N JEFFERSON ST"
19,"N MADISON ST"
21,"QUAIL CREEK DR"
24,"INDIAN DR"
12,"CHEROKEE TRAIL"
50,"CORONADO TRAIL"
'''
import wx, os
from cStringIO import StringIO
import csv
class MainFrame(wx.Frame):
def __init__(self, parent, choices=[], style=0):
wx.Frame.__init__(self,None,wx.ID_ANY,title='test combo autocomplete',size=(225, 70))
self.vbox= wx.BoxSizer(wx.VERTICAL)
self.background = wx.Panel(self)
self.OpenDir = wx.TextCtrl(self,style=wx.PROCESS_ENTER|wx.TE_CENTRE)
self.filter = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_DROPDOWN)
self.OpenDir.Bind(wx.EVT_LEFT_UP,self.OnChooseRoot)
self.filter.Bind(wx.EVT_TEXT, self.OnTextChanged)
hsizer1 = wx.BoxSizer(wx.HORIZONTAL)
hsizer1.Add(self.OpenDir,1)
hsizer2 = wx.BoxSizer(wx.HORIZONTAL)
hsizer2.Add(self.filter,1)
self.vbox.Add(hsizer1,proportion = 0,flag = wx.EXPAND)
self.vbox.Add(hsizer2,proportion = 0,flag = wx.EXPAND)
self.SetSizer(self.vbox)
self.Show()
self.OpenDir.SetValue("click to open directory")
def OnTextChanged(self, event):
def refresh():
wnd = self.filter
currentText = event.GetString()
while wnd:
print currentText
wnd.Layout()
print wnd.Layout()
wnd = wnd.GetParent()
self.filter.SetInsertionPoint(len(currentText))
self.filter.SetMark(len(currentText), len(self.choices))
self.filter.Refresh()
refresh()
event.Skip()
def OnChooseRoot(self, event):
self.dirname=""
dlg = wx.FileDialog(self, "choose a file to open", self.dirname,
"", "*.*", wx.OPEN)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
self.pathname = dlg.GetPath()
self.f = open(os.path.join(self.dirname, self.filename), 'r')
self.text = self.f.read()
labeltop = self.dirname + '\\'
self.OpenDir.SetValue(labeltop + self.filename)
sources = [StringIO(self.text)]
for i, source in enumerate(sources):
c = list(csv.reader(source))
self.choices = [x[1] for x in c]
self.filter.SetItems(self.choices)
app = wx.App(redirect=False)
frame = MainFrame(None)
app.MainLoop()

App Engine - Datastore - Python: Delete element within a StructuredProperty

I have a StructuredProperty that looks like this:
userDB(key=Key('userDB', 5580090230439936), name=u'Super User', orgs=[providers(name=u'Comp, Inc.', password=u'1111111', url=None, username=u'111111', value=u'comp'), providers(name=u'Systems, Inc.', password=u'2222222', url=None, username=u'222222', value=u'system')], update=None, userID=u'super#example.com')
I would like to delete every provider who's 'value' == 'system'.
class providers(EndpointsModel):
name = ndb.StringProperty()
value = ndb.StringProperty()
url = ndb.StringProperty()
username = ndb.StringProperty()
password = ndb.StringProperty()
class userDB(EndpointsModel):
userID = ndb.StringProperty(required=True, indexed=True)
name = ndb.StringProperty(required=True, indexed=True)
update = ndb.DateTimeProperty(auto_now_add=True, indexed=True)
orgs = ndb.StructuredProperty(providers, repeated=True, indexed=True)
system = ndb.StructuredProperty(system, repeated=True, indexed=True)
comp = ndb.StructuredProperty(comp, repeated=True, indexed=True)
I tried this:
def delOrgs(key, X): #Key is a userDB key and X is a list ['system']
for B in X:
for A in key[0].get().orgs:
del_provider = key[0].get().query(A.value == B).fetch(keys_only=True)
#del_provider[0].delete()
logging.info(del_provider)
but i get the following error:
TypeError: Cannot filter a non-Node argument; received False
Any help would be greatly appreciated.
Your query should look like:
userDB.query(userDB.orgs.value == 'system)
This will return all of the userDBs which have a provider with value == 'system'.
You'll then need to update the 'orgs' property of each, removing any that you don't want, and then re-put the entities:
users = query.fetch()
for user in users:
user.orgs = filter(lambda provider: provider.value != 'system', user.orgs)
ndb.put_multi(users)
Structured properties don't (or shouldn't) exist as independent entities, so you can't fetch them independently of the entity that contains them, and can't delete them directly.

Resources