Masking textfield for password in Maya UI using Python - python

I have created a UI in Maya to send some information for rendering in a mail. For that the user has to type different data including username and password. But when typing the password, it is visible like the other texts. Is there a way where the password could appear like dots or asterisks?? The script is written in Python. Below is the image of the UI.

You could set the changeCommand of the text field to a function that would save the text to a variable and replace the text shown with an asterisk for each character.
import pymel.core as pm
class myWindow():
def __init__(self):
self.password = ''
win = pm.window(title='Test')
lo = pm.columnLayout()
self.pswdField = pm.textField(changeCommand=self.hideText)
win.show()
def hideText(self, *args):
self.password = self.pswdField.getText()
self.pswdField.setText("*" * len(self.pswdField.getText()))
That's just one way and it's not very robust..but I would definitely look into Qt like Daniel pointed out.

For my code, I tried something like this:
self.Passwd.keyPressEvent = lambda event : self.encryption()
def encryption(self):
self.a = "*"
self.Passwd.setText(self.Passwd.text() + self.a)
This is just for encoding the characters with * as you type.
You still need to capture the input into a variable or something.
And somehow segregate the keys that are not allowed in the password.
hope it gives a start.

I found the below function best suited for the above problem:
self.Passwd = QLineEdit("Password")
self.Passwd.setEchoMode(QLineEdit.Password)

If Qt is too much trouble and you don't need it 100% reliable this might suffice. It's similar to the answer from Argiri, but it replaces text as it is typed, and appends each keypress to a variable.
import pymel.core as pm
class LoginWindow():
def __init__(self):
self.password = ""
self.win = pm.window()
lo = pm.columnLayout()
self.userField = pm.textField()
self.passField = pm.textField(textChangedCommand=self.obscure)
self.goButton = pm.button("Login", c=self.go)
self.win.show()
def obscure(self, *args):
new_pw = self.passField.getText()
if len(new_pw) > len(self.password):
self.password+=new_pw[-1]
elif len(new_pw) < len(self.password):
self.password = self.password[0:-1]
self.passField.setText(u"\u2022" * len(self.password))
def go(self, *args):
print ("u: {} p: {}".format(self.userField.getText(), self.password))
pm.deleteUI(self.win)
lw = LoginWindow()
Note that if the user tried to edit the middle of the password, or tried to type it in the wrong order the reported password would be mangled. I'm not sure if that's a problem in the real world.

Related

Python CLR Winforms - Passing data between .NET Winforms

I have a fairly simple task that has eluded me when using Python to generate and automate .NET WinForms. How do I pass data between forms?
I've tried everything: using global variables, using immutable strings, etc. and nothing seems to stick. Can someone show me an example, send me a link, or let me know what I am doing wrong? I have been at this for over a week and frustration is starting to mount.
Below is a (sloppy) example of taking data from one form - a string - and sending it to another form in a Textbox.
MYSTRING = ''
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import *
from System.Drawing import *
class MyForm(Form):
def __init__(self):
self.Text1 = TextBox()
self.Button1 = Button()
self.Button1.Location = Point(0, self.Text1.Bottom + 10)
self.Button1.Text = 'Send'
self.Controls.Add(self.Text1)
self.Controls.Add(self.Button1)
self.Button1.Click += self.Button1_Click
def Button1_Click(self, sender, args):
MYSTRING = self.Text1.Text
self.TopLevel = False
f2 = MyForm2()
f2.Show()
self.TopLevel = True
class MyForm2(Form):
def __init__(self):
self.Text2 = TextBox()
self.Controls.Add(self.Text2)
self.Load += self.MyForm2_Load
def MyForm2_Load(self, sender, args):
self.Text2.Text = MYSTRING
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(MyForm())
So, I figured it out...again.
I had to set a python global variable within one of my events that triggers an event, like so...
def dgvExpanderInfo_CellDoubleClick_Event(self, sender, args):
global SelectedExpanderData_List
...
...then I could access whatever is in that globabl variable - in this case it was a list.
def MyForm2_Form_Load_Event(self, sender, args):
self.textbox1.Text = SelectedExpanderData_List[0]
self.textbox2.Text = SelectedExpanderData_List[1]
self.textbox3.Text = SelectedExpanderData_List[2]
...
I hope this helps others as I have found no real documentation on this anywhere.

Dynamically add and remove list items in an IPython widget

I'm trying to achieve a basic todo list app in Ipython Jupyter Notebook using ipywidgets.
I can easily achieve the functionality of adding items to my list, however, I can't properly handle removing of existing items if the 'Remove' button is clicked. The entire code is run in a single cell.
import ipywidgets as widgets
from ipywidgets import VBox, HBox, Text, Button
from IPython.display import display
todo = []
def completed_sentence(sentence):
""" To display existing notes with a 'Remove' button """
sentenceField = Text(value=sentence)
removeButton = Button(description='Remove',
button_style='danger')
return HBox([sentenceField, removeButton])
def render_sentences(_):
""" To update the view """
global a,b
if a.value != '':
todo.append(a.value)
a.value = ''
todoWidget.children = tuple\
([VBox([VBox([completed_sentence(each)
for each in todo]),
HBox([a, b])])])
# Setting up a basic view- with an empty field and a button
a = widgets.Text(value='')
b = widgets.Button(description='Add')
b.on_click(render_sentences)
todoWidget = widgets.HBox([a, b])
display(todoWidget)
Now, in order to enable the removal of sentences, I update the definition of the function completed_sentence as follows:
def completed_sentence(sentence):
""" To display existing notes """
def remove_sentence(_):
global render_sentences
try:
if todo.index(sentenceField.value) >= 0:
todo.remove(sentenceField.value)
render_sentences()
except:
return
sentenceField = Text(value=sentence)
removeButton = Button(description='Remove', button_style='danger')
removeButton.on_click(remove_sentence)
return HBox([sentenceField, removeButton])
But now, this has the issue that its call to render_sentences is ignored! What is the optimal way to deal with such a kind of 'reactive' programming, if you will, using Ipython Widgets.
Updating the definition of completed_sentence seems to do the job. But it still remains a mystery why the original definition didn't work.
def completed_sentence(sentence):
def remove_sentence(_):
global render_sentences
try:
if todo.index(sentenceField.value) >= 0:
todo.remove(sentenceField.value)
except:
pass
render_sentences(_)
sentenceField = Text(value=sentence)
removeButton = Button(description='Remove', button_style='danger')
removeButton.on_click(remove_sentence)
sentence_view = HBox([sentenceField, removeButton])
return sentence_view

How to return a string instead of PY_VAR

HELP! Still learning Python here. Why am I getting PY_VAR1 returned instead of the string in "QUOTE1" (in an external file). And, is there any way to clear the screen for the next instance to display. As of right now, the quotes keep stacking on top of each other instead of presenting one at a time. Thank you for the help!
def updateQuote(self):
self.quoteNumber = 1
if (self.quoteNumber <=10):
quote = "QUOTE"+str(self.quoteNumber)
self.quote=StringVar()
self.quote.set(self.quote)
self.msg = Message(self.window,textvariable=self.quote,
width=300,font=("Aria",24))
self.quoteNumber+=1
self.msg.pack(side=TOP)
self.window.after(3000,self.updateQuote)
PY_VAR1 is shown because self.quote is passed to self.quote.set(). quote should be used instead.
In order to show only one instance of quote, self.msg should be created once and update its text inside updateQuote() via self.quote.
Also you should not reset self.quoteNumber to 1 in every run of updateQuote().
def __init__(self):
...
# moved from self.updateQuote()
self.quote = StringVar()
self.msg = Message(self.window, textvariable=self.quote, width=300, font=("Aria",24))
self.msg.pack(side=TOP)
...
# start the quote display
self.updateQuote()
def updateQuote(self, quoteNumber=1):
if quoteNumber <= 10:
quote = "QUOTE" + str(quoteNumber)
self.quote.set(quote) # update self.msg
self.window.after(3000, self.updateQuote, quoteNumber+1)

using a string variable to control a textbox property

I have various textboxes on a form to enter numbers and want to have a single function which can check the entry and highight the box in red if incorrect rather than repeating the same code for each textbox.
Is it possible to set the name of the textbox (_FreqTextBox) as a variable so I can make this a generic function for each textbox. An example function is below. I've tried to find examples of this but no luck so far. Newish to Python so apologies if this is obvious.
def FreqTextBoxTextChanged(self, sender, e):
self._FreqTextBox.BackColor = System.Drawing.SystemColors.Window
IsNumber,text_value = self.CheckNumber(self._FreqTextBox.Text)
if (IsNumber == False):
self._FreqTextBox.BackColor = System.Drawing.Color.Red
return
IsNumber = self.NegativeValueMsg(text_value)
if (IsNumber == True):
self._FreqTextBox.BackColor = System.Drawing.Color.Red
return

Problems with a bind function from tkinter in Python

I am working on an application that is supposed to support both running from a console and from a GUI. The application has several options to choose from, and since in both running modes the program is going to have the same options obviously, I made a generalisation:
class Option:
def __init__(self, par_name, par_desc):
self.name = par_name
self.desc = par_desc
class Mode():
def __init__(self):
self.options = []
self.options.append(Option('Option1', 'Desc1'))
self.options.append(Option('Option2', 'Desc2'))
self.options.append(Option('Option3', 'Desc3'))
self.options.append(Option('Option4', 'Desc4'))
self.options.append(Option('Option5', 'Desc5'))
#And so on
The problem is that in GUI, those options are going to be buttons, so I have to add a new field to an Option class and I'm doing it like this:
def onMouseEnter(par_event, par_option):
helpLabel.configure(text = par_option.desc)
return
def onMouseLeave(par_event):
helpLabel.configure(text = '')
return
class GUIMode(Mode):
#...
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name, bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event, iOption))
iOption.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
#...
There is also a "help label" showing the description of the option every time a mouse hovers over it, so there I am binding those functions.
What is happening is that while I am indeed successfully adding a new field with a button, the bind function seems to mess up and the result is this:
Help label is always showing the description of the last option added, no matter over which button I hover. The problem seems to go away if I directly modify the Option class instead, like this:
class Option:
def __init__(self, par_name, par_desc):
self.name = par_name
self.desc = par_desc
self.button = Button(wrapper, text = self.name, bg = '#004A7F', fg = 'white')
self.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event, self))
self.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
But I obviously can't keep it that way because the console mode will get those fields too which I don't really want. Isn't this the same thing, however? Why does it matter if I do it in a constructor with self or in a loop later? I therefore assume that the problem might be in a way I dynamically add the field to the class?
Here is the full minimal and runnable test code or whatever it is called, if you want to mess with it: http://pastebin.com/0PWnF2P0
Thank you for your time
The problem is that the value of iOption is evaluated after the
for iOption in self.option:
loops are complete. Since you reset iOption on each iteration, when the loop is completed iOption has the same value, namely the last element in self.options. You can demonstrate this at-event-time binding with the snippet:
def debug_late_bind(event):
print(iOption)
onMouseEnter(event, iOption)
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name,
bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', debug_late_bind)
which will show that all events that iOption has the same value.
I split out the use of iOption to debug_late_bind to show that iOption comes in from the class scope and is not evaluated when the bind() call is executed. A more simple example would be
def print_i():
print(i)
for i in range(5):
pass
print_i()
which prints "4" because that is the last value that was assigned to i. This is why every call in your code to onMouseEnter(par_event, iOption) has the same value for iOption; it is evaluated at the time of the event, not the time of the bind. I suggest that you read up on model view controller and understand how you've tangled the view and the controller. The primary reason this has happened is that you've got two views (console and tk) which should be less coupled with the model.
Extracting the .widget property of the event is a decent workaround, but better still would be to not overwrite the scalar iOption, but instead use list of individual buttons. The code
for n, iOption in enumerate(self.options):
would help in creating a list. In your proposed workaround, you are encoding too much of the iOption model in the tkinter view. That's bound to bite you again at some point.
I don't know what the actual problem was with my original code, but I kind of just bypassed it. I added a dictionary with button as a key and option as a value and I just used the par_event.widget to get the option and it's description, which is working fine:
buttonOption = {}
def onMouseEnter(par_event):
helpLabel.configure(text = buttonOption[par_event.widget].desc)
return
def onMouseLeave(par_event):
helpLabel.configure(text = '')
return
class GUIMode(Mode):
def run(self):
#...
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name, bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event))
iOption.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
buttonOption[iOption.button] = iOption
#...

Categories