I would like to observe an ipywidget text input
out4 = wd.Output()
wd_input_text4 = wd.Text(value="",
placeholder='placeholder',
description='Number:',
)
def method4(sender):
with out4:
out4.clear_output()
print(wd_input_text4.value)
wd_input_text4.observe(method4, names=['value'], type='change')
display(wd.VBox([wd_input_text4,out4]))
What I would like is to pass to the handler an extra variable, just call it A.
pseudocode would be (it does not work):
def method4(sender, A):
with out4:
out4.clear_output()
print(wd_input_text4.value, A)
wd_input_text4.observe(method4, names=['value'], type='change', A)
Is that even possible?
How can I do it?
class Method4:
def __init__(self,ctx):
self.ctx = ctx
def method4(self, sender):
with out4:
out4.clear_output()
print(wd_input_text4.value, self.ctx)
wd_input_text4.observe(Method4(A).method4, names=['value'], type='change')
Using functools.partial would avoid a class to hold the extra state.
import ipywidgets as widgets
import functools
a = widgets.IntText()
def observe_val(to_add, widg):
val = widg['new']
print(val + to_add)
a.observe(functools.partial(observe_val, 4), names=['value'])
display(a)
Related
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.
I am trying to find an efficient and secure way to call different functions based on the transaction name the user enters. There are a 100+ different transactions. A 100 "IF" would do the job, however, I want to find a more efficent way to call the transaction. The "eval" would do it, but I read that this should not be used, as the user can enter any transaction name.
from operator import methodcaller
import sys
from PyQt5.QtWidgets import (QMainWindow,QToolBar,QLineEdit,
QLabel, QApplication)
def one():
print ("1")
def two():
print ("2")
def three():
print("3")
class main_menu(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
menuBar = self.menuBar()
self.ToolBar = QToolBar()
self.ToolBar.setMovable(False)
self.addToolBar(self.ToolBar)
self.tcode = QLineEdit(maxLength=5)
self.tcode.returnPressed.connect(self.tcode_action)
self.ToolBar.addWidget(QLabel(" Transaction : "))
self.ToolBar.addWidget(self.tcode)
def tcode_action(self):
## if self.tcode.text() == "one":
## one()
## if self.tcode.text() == "two":
## two()
## if self.tcode.text() == "three":
## three()
## eval(self.tcode.text()+"()")
def main(args):
app = QApplication(sys.argv)
mm = main_menu()
mm.show()
sys.exit(app.exec_())
if __name__=="__main__":
main(sys.argv)
Global variables can be accessed via globals() in python.
You can use:
def tcode_action(self):
fn = globals().get(self.tcode.text())
if fn:
fn()
else:
print("invalid input")
One option could be to use a QComboBox to restrict the function set. You can also use an Enum to enumerate valid functions.
from enum import Enum
from functools import partial
# function definitions
def fcn_1( x ):
print( 'F1' )
def fcn_2( x ):
print( 'F2' )
# valid functions Enum
# Must wrap functions in partial so they are not defined as methods.
# See below post for more details.
class ValidFunctions( Enum ):
Echo = partial( fcn_1 )
Increment = partial( fcn_2 )
# function selection ComboBox
cb = QComboBox()
cb.addItem( 'Echo' )
cb.AddItem( 'Increment' )
# connecting the signal
def call_function():
fcn = ValidFunctions[ cb.currentText() ]
fcn()
cb.currentIndexChanged.connect( call_function )
Note: I haven't debugged this code.
How to define enum values that are functions
I will do this with this code now:
def tcode_action(self):
try:
func = getattr(self,self.tcode.text())
func()
except:
pass
Any comments to this?
Let's say I want a widget composed of an IntText widget and a DropDown widget which value is a concatened string of those widgets values. How can I do?
Here is an attempt:
import re
import ipywidgets as ipw
from IPython.display import display
class IntMultipliedDropdown:
_VALUE_PATTERN = re.compile('(?P<num>\d+) (?P<option>\w+-?\w*)')
def __init__(self, options, option_value, int_value=1):
self.number = ipw.IntText(int_value)
self.options = ipw.Dropdown(options=options, value=option_value)
self.box = ipw.HBox([self.number, self.options])
self.number.observe(self._on_changes, names='value')
self.options.observe(self._on_changes, names='value')
self._handelers = []
def _on_changes(self, change):
for handeler in self._handelers:
handeler(self.value)
#property
def value(self):
return "{} {}".format(self.number.value, self.options.value)
#value.setter
def value(self, value):
match = re.search(self._VALUE_PATTERN, value)
groupdict = match.groupdict()
self.number.value = groupdict['num']
self.options.value = groupdict['option']
def _ipython_display_(self, **kwargs):
return self.box._ipython_display_(**kwargs)
def observe(self, handler):
if handler not in self._handelers:
self._handelers.append(handler)
mywidget = IntMultipliedDropdown(['apple', 'bed', 'cell'], 'cell')
mywidget.observe(print)
display(mywidget)
print('default value:', mywidget.value)
mywidget.value = '2 bed'
It works but has drawbacks. First, when I set mywidget.value the observed function is called two times: on number value change and on option value change.
Second and worst is that I cannot use this widget in a Box widget like:
ipw.HBox([ipw.Label('Mylabel'), mywidget])
Which raises:
ValueError: Can't clean for JSON: <__main__.IntMultipliedDropdown object at 0x7f7d604fff28>
Is there a better solution?
The class you created is not a widget, although you did mimick some of the behaviors (observe, display). This is probably why you couldn't get it to display in an HBox. If you want to create a new widget, inherit from ipyw.Widget or any other widget.
You have two underlying widgets that are being listened to, so it is normal that two functions get called when you change their values. If you want only one function to be called, listen directly to the value of your new widget.
This is how you could do it, by inheriting from HBox:
import re
import ipywidgets as ipw
from traitlets import Unicode
from IPython.display import display
class IntMultipliedDropdown(ipw.HBox):
_VALUE_PATTERN = re.compile('(?P<num>\d+) (?P<option>\w+-?\w*)')
value = Unicode()
def __init__(self, options, option_value, int_value=1, **kwargs):
self.number = ipw.IntText(int_value)
self.options = ipw.Dropdown(options=options, value=option_value)
self._update_value()
self.number.observe(self._update_value, names='value')
self.options.observe(self._update_value, names='value')
self.observe(self._update_children, names='value')
super().__init__(children=[self.number, self.options], **kwargs)
def _update_children(self, *args):
match = re.search(self._VALUE_PATTERN, self.value)
groupdict = match.groupdict()
self.number.value = groupdict['num']
self.options.value = groupdict['option']
def _update_value(self, *args):
self.value = "{} {}".format(self.number.value, self.options.value)
mywidget = IntMultipliedDropdown(['apple', 'bed', 'cell'], 'cell')
display(mywidget)
There likely is a reason why you went to all the trouble of creating a new widget, but why not use the interactive function?
Something like:
import ipywidgets as ipw
from ipywidgets import *
w_number = ipw.IntText(value = 1)
w_options = ipw.Dropdown(options = ['apple', 'bed', 'cell'], value ='cell')
mywidget_value = ''
def display_value(number, options):
mywidget_value = str(number)+' '+options
#print(mywidget_value)
return mywidget_value
w_box = interactive(display_value, number=w_number, options=w_options)
display(w_box)
Then you have aBox, and you can adapt its layout. You can also access the keyword arguments with w_box.kwargs or the return value of the function with w_box.result, which is the concatenated string of the 2 widgets that you were looking for...
Here is an example of failure from a shell.
>>> from traits.api import Dict
>>> d=Dict()
>>> d['Foo']='BAR'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Dict' object does not support item assignment
I have been searching all over the web, and there is no indication of how to use Dict.
I am trying to write a simple app that displays the contents of a python dictionary. This link (Defining view elements from dictionary elements in TraitsUI) was moderately helpful except for the fact that the dictionary gets updated on some poll_interval and if I use the solution there (wrapping a normal python dict in a class derived from HasTraits) the display does not update when the underlying dictionary gets updated.
Here are the relevant parts of what I have right now. The last class can pretty much be ignored, the only reason I included it is to help understand how I intend to use the Dict.
pyNetObjDisplay.run_ext() gets called once per loop from the base classes run() method
class DictContainer(HasTraits):
_dict = {}
def __getattr__(self, key):
return self._dict[key]
def __getitem__(self, key):
return self._dict[key]
def __setitem__(self, key, value):
self._dict[key] = value
def __delitem__(self, key, value):
del self._dict[key]
def __str__(self):
return self._dict.__str__()
def __repr__(self):
return self._dict.__repr__()
def has_key(self, key):
return self._dict.has_key(key)
class displayWindow(HasTraits):
_remote_data = Instance(DictContainer)
_messages = Str('', desc='Field to display messages to the user.', label='Messages', multi_line=True)
def __remote_data_default(self):
tempDict = DictContainer()
tempDict._dict = Dict
#tempDict['FOO'] = 'BAR'
sys.stderr.write('SETTING DEFAULT DICTIONARY:\t%s\n' % tempDict)
return tempDict
def __messages_default(self):
tempStr = Str()
tempStr = ''
return tempStr
def traits_view(self):
return View(
Item('object._remote_data', editor=ValueEditor()),
Item('object._messages'),
resizable=True
)
class pyNetObjDisplay(pyNetObject.pyNetObjPubClient):
'''A derived pyNetObjPubClient that stores remote data in a dictionary and displays it using traitsui.'''
def __init__(self, hostname='localhost', port=54322, service='pyNetObject', poll_int=10.0):
self._display = displayWindow()
self.poll_int = poll_int
super(pyNetObjDisplay, self).__init__(hostname, port, service)
self._ui_running = False
self._ui_pid = 0
### For Testing Only, REMOVE THESE LINES ###
self.connect()
self.ns_subscribe(service, 'FOO', poll_int)
self.ns_subscribe(service, 'BAR', poll_int)
self.ns_subscribe(service, 'BAZ', poll_int)
############################################
def run_ext(self):
if not self._ui_running:
self._ui_running = True
self._ui_pid = os.fork()
if not self._ui_pid:
time.sleep(1.25*self.poll_int)
self._display.configure_traits()
for ((service, namespace, key), value) in self._object_buffer:
sys.stderr.write('TEST:\t' + str(self._display._remote_data) + '\n')
if not self._display._remote_data.has_key(service):
self._display._remote_data[service] = {}
if not self._display._remote_data[service].has_key(namespace):
#self._remote_data[service][namespace] = {}
self._display._remote_data[service][namespace] = {}
self._display._remote_data[service][namespace][key] = value
msg = 'Got Published ((service, namespace, key), value) pair:\t((%s, %s, %s), %s)\n' % (service, namespace, key, value)
sys.stderr.write(msg)
self._display._messages += msg
sys.stderr.write('REMOTE DATA:\n' + str(self._display._remote_data)
self._object_buffer = []
I think your basic problem has to do with notification issues for traits that live outside the model object, and not with "how to access those objects" per se [edit: actually no this is not your problem at all! But it is what I thought you were trying to do when I read your question with my biased mentality towards problems I have seen before and in any case my suggested solution will still work]. I have run into this sort of problem recently because of how I decided to design my program (with code describing a GUI separated modularly from the very complex sets of data that it can contain). You may have found my other questions, as you found the first one.
Having lots of data live in a complex data hierarchy away from the GUI is not the design that traitsui has in mind for your application and it causes all kinds of problems with notifications. Having a flatter design where GUI information is integrated into the different parts of your program more directly is the design solution.
I think that various workarounds might be possible for this in general (I have used some for instance in enabled_when listening outside model object) that don't involve dictionaries. I'm not sure what the most design friendly solution to your problem with dictionaries is, but one thing that works and doesn't interfere a lot with your design (but it is still a "somewhat annoying" solution) is to make everything in a dictionary be a HasTraits and thus tag it as listenable. Like so:
from traits.api import *
from traitsui.api import *
from traitsui.ui_editors.array_view_editor import ArrayViewEditor
import numpy as np
class DContainer(HasTraits):
_dict=Dict
def __getattr__(self, k):
if k in self._dict:
return self._dict[k]
class DItem(HasTraits):
_item=Any
def __init__(self,item):
super(DItem,self).__init__()
self._item=item
def setitem(self,val):
self._item=val
def getitem(self):
return self._item
def traits_view(self):
return View(Item('_item',editor=ArrayViewEditor()))
class LargeApplication(HasTraits):
d=Instance(DContainer)
stupid_listener=Any
bn=Button('CLICKME')
def _d_default(self):
d=DContainer()
d._dict={'a_stat':DItem(np.random.random((10,1))),
'b_stat':DItem(np.random.random((10,10)))}
return d
def traits_view(self):
v=View(
Item('object.d.a_stat',editor=InstanceEditor(),style='custom'),
Item('bn'),
height=500,width=500)
return v
def _bn_fired(self):
self.d.a_stat.setitem(np.random.random((10,1)))
LargeApplication().configure_traits()
Okay, I found the answer (kindof) in this question: Traits List not reporting items added or removed
when including Dict or List objects as attributes in a class one should NOT do it this way:
class Foo(HasTraits):
def __init__(self):
### This will not work as expected!
self.bar = Dict(desc='Description.', label='Name', value={})
Instead do this:
class Foo(HasTraits):
def __init__(self):
self.add_trait('bar', Dict(desc='Description.', label='Name', value={}) )
Now the following will work:
>>> f = Foo()
>>> f.bar['baz']='boo'
>>> f.bar['baz']
'boo'
Unfortunately for some reason the GUI generated with configure_traits() does not update it's view when the underlying data changes. Here is some test code that demonstrates the problem:
import os
import time
import sys
from traits.api import HasTraits, Str, Dict
from traitsui.api import View, Item, ValueEditor
class displayWindow(HasTraits):
def __init__(self, **traits):
super(displayWindow, self).__init__(**traits)
self.add_trait('_remote_data', Dict(desc='Dictionary to store remote data in.', label='Data', value={}) )
self.add_trait('_messages', Str(desc='Field to display messages to the user.', label='Messages', multi_line=True, value='') )
def traits_view(self):
return View(
Item('object._remote_data', editor=ValueEditor()),
Item('object._messages'),
resizable=True
)
class testObj(object):
def __init__(self):
super(testObj, self).__init__()
self._display = displayWindow()
self._ui_pid = 0
def run(self):
### Run the GUI in the background
self._ui_pid = os.fork()
if not self._ui_pid:
self._display.configure_traits()
i = 0
while True:
self._display._remote_data[str(i)] = i
msg = 'Added (key,value):\t("%s", %s)\n' % (str(i), i, )
self._display._messages += msg
sys.stderr.write(msg)
time.sleep(5.0)
i+=1
if __name__ == '__main__':
f = testObj()
f.run()
Is there trivial or elegant way to differentiate between many same-type signal sources in PySide/PyQt?
I am learning PySide. I have written simple application, which multiplies two numbers from two different QLineEdit() objects. Result is displayed in third QLineEdit.
Multiplier and multiplicand QLineEdit.textChanged() signals are connected to one method (TxtChanged). In this method i have to differentiate between signal sources. After some trials I figured out some workaround based upon placeholder text (4 lines below "is there another way?" comment in my code)
code:
import sys
from PySide import QtGui, QtCore
class myGUI(QtGui.QWidget):
def __init__(self, *args, **kwargs):
QtGui.QWidget.__init__(self, *args, **kwargs)
self.multiplier = 0
self.multiplicand = 0
self.myGUIInit()
def myGUIInit(self):
# input forms
a1_label = QtGui.QLabel("a1")
a1_edit = QtGui.QLineEdit()
a1_edit.setPlaceholderText("a1")
a2_label = QtGui.QLabel("a2")
a2_edit = QtGui.QLineEdit()
a2_edit.setPlaceholderText("a2")
# output form
a1a2_label = QtGui.QLabel("a1*a2")
self.a1a2_edit = QtGui.QLineEdit()
self.a1a2_edit.setReadOnly(True)
# forms events
a1_edit.textChanged.connect(self.TxtChanged)
a2_edit.textChanged.connect(self.TxtChanged)
# grid
grid = QtGui.QGridLayout()
grid.setSpacing(10)
grid.addWidget(a1_label,1,0)
grid.addWidget(a1_edit,1,1)
grid.addWidget(a2_label,2,0)
grid.addWidget(a2_edit,2,1)
grid.addWidget(a1a2_label,3,0)
grid.addWidget(self.a1a2_edit,3,1)
self.setLayout(grid)
self.setGeometry(100,100,200,200)
self.setWindowTitle("a*b")
self.show()
def TxtChanged(self,text):
sender = self.sender()
sender_text = sender.text()
if sender_text == '': sender_text = '0'
# is there another way?
if sender.placeholderText() == 'a1':
self.multiplicand = sender_text
else:
self.multiplier = sender_text
product = int(self.multiplier) * int(self.multiplicand)
print(self.multiplier,self.multiplicand,product)
self.a1a2_edit.setText(str(product))
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = myGUI()
sys.exit(app.exec_())
main()
best regards,
ostrzysz
You can use the functools.partial function - and therefore connect your signals to straight to your method/function but rather to a python object which will automatically call your function with some extra data you pass it:
from functools import partial
...
....
a1_edit.textChanged.connect(partial(self.TxtChanged, a1_edit))
a2_edit.textChanged.connect(partial(self.TxtChanged, a2_edit))
...
def TxtChanged(self,sender, text):
# and here you have the "sender" parameter as it was filled in the call to "partial"
...
partials is part of the stdlib, and is very readable, but one can always use lambda instead of partial for the same effect -
a1_edit.textChanged.connect(lambda text: self.TxtChanged(a1_edit, text))
In this way the object yielded by the lambda expression will be a temporary function that will use the values for "self" and "a1_edit" from the current local variables (at the time the button is clicked), and the variable named "text" will be supplied by Pyside's callback.
One thing that bugs me most in your code is that you are using placeholderText to differentiate. QObjects has another property called objectName that is more suitable for your task. And, you don't need to use sender.text() to get the text of QLineEdit. textChanged already sends it, so you will have it in your text parameter.
Also, using a dictionary instead of two separate variables (multiplier and multiplicand) will simplify your code further.
Here is the changed code:
class myGUI(QtGui.QWidget):
def __init__(self, *args, **kwargs):
QtGui.QWidget.__init__(self, *args, **kwargs)
self.data = {"multiplier": 0,
"multiplicand": 0}
self.myGUIInit()
def myGUIInit(self):
a1_label = QtGui.QLabel("a1")
a1_edit = QtGui.QLineEdit()
a1_edit.setObjectName("multiplicand")
a2_label = QtGui.QLabel("a2")
a2_edit = QtGui.QLineEdit()
a2_edit.setObjectName("multiplier")
# skipped the rest because same
def TxtChanged(self, text):
sender = self.sender()
# casting to int while assigning seems logical.
self.data[sender.objectName()] = int(text)
product = self.data["multiplier"] * self.data["multiplicand"]
print(self.data["multiplier"], self.data["multiplicand"], product)
self.a1a2_edit.setText(str(product))
Although #jsbueno and #Avaris answered your direct question about signal sources, I wouldn't relay on this sources in your concrete case. You can make instance members a1_edit and a2_edit:
...
self.a1_edit = QtGui.QLineEdit()
...
self.a2_edit = QtGui.QLineEdit()
...
It will simplify your TxtChanged function:
def TxtChanged(self,text):
try:
multiplier = int(self.a1_edit.text())
multiplicand = int(self.a2_edit.text())
except ValueError:
self.a1a2_edit.setText('Enter two numbers')
return
product = multiplier * multiplicand
print(multiplier, multiplicand, product)
self.a1a2_edit.setText(str(product))
Also, instead of handling ValueError exception, you can use QIntValidator for input controls:
self.int_validator = QtGui.QIntValidator()
self.a1_edit.setValidator(self.int_validator)
self.a2_edit.setValidator(self.int_validator)