Why attribute doesn't update in traitsui on user input - python

I have a simple program that gets an text input from the user, and displays it on an instrument (Keithley). However, the attribute does not seem to change. That is, when I run the Start method, the output is "Wowee", even if I change Display in a pop-up window. Do I have to make it editable? I didn't think so. I am basically following Gael Varoquaux's intro to traits.
COntrolPanel.py:
from traits.api import *
from traitsui.api import *
import Keithley3706A_module
class ControlPanel(HasTraits):
keithley2430settings = Instance(Keithley2430.Keithley2430Settings, ())
keithley3706Asettings = Instance(Keithley3706A_module.Keithley3706ASettings, ())
start = Button("Start Measurements")
clear_3706A_display = Button("Clear K3706A Display")
k3706A_settings = Keithley3706A_module.Keithley3706ASettings()
k3706A = Keithley3706A_module.Keithley3706A()
view = View(Item('start', show_label=False,style='custom' ),
Item('clear_3706A_display', show_label=False,style='custom' ),
Item('keithley2430settings',style='custom'),
Item('keithley3706Asettings',style='simple'))
def _start_fired(self):
print "hello %s" % self.k3706A_settings.display
self.k3706A.message(self.k3706A_settings.display)
def _clear_3706A_display_fired(self):
self.k3706A.clear()
if __name__ == '__main__':
ControlPanel().configure_traits()
Keithley3706A.py:
from traits.api import *
from traitsui.api import *
import visa
import time
class Keithley3706ASettings(HasTraits):
display = String("Wowee")
class Keithley3706A(HasTraits):
def __init__(self):
self.Keithley3706AUSB = visa.instrument("USB0::0x05E6::0x3706::04019447::INSTR")
def message(self,foo):
s="display.settext('%s')" % foo
self.Keithley3706AUSB.write("display.clear()")
self.Keithley3706AUSB.write(s)
def clear(self):
self.Keithley3706AUSB.write("display.clear()")

In the ControlPanel class, you have created two different instances of Keithley3706ASettings, namely keithley3706Asettings and k3706A_settings. If I delete the latter, and replace the use of k3706A_settings with keithley3706Asettings, it works. Here's my version (with references to the 2430 device removed, the k3706A trait declared as an Instance of Keithley3706A, and a few other irrelevant UI changes):
class ControlPanel(HasTraits):
keithley3706Asettings = Instance(Keithley3706A_module.Keithley3706ASettings, ())
k3706A = Instance(Keithley3706A_module.Keithley3706A, ())
start = Button("Start Measurements")
clear_3706A_display = Button("Clear K3706A Display")
view = View(UItem('start'),
UItem('clear_3706A_display'),
Item('keithley3706Asettings', style='simple'))
def _start_fired(self):
print "hello %s" % self.keithley3706Asettings.display
self.k3706A.message(self.keithley3706Asettings.display)
def _clear_3706A_display_fired(self):
self.k3706A.clear()

Related

Update progress bar - MVP pattern

I'm studying the MVP pattern but having a hard time following the principles in order to update in real time a progress bar. As I understand the Presenter checks if there's any update in the Model and then outputs the result, so there's no instantiation of the Presenter in the Model, only the Presenter should instantiate the Model and the View.
My question is: how should I update the progress bar by following the MVP principle?
I could of course call presenter.update_progress_bar(i, total) from Model, but then it would infringe the MVP principle.
Here's a minimal working example:
PS: for now, I'm using CLI.
/main.py
import modules
def main():
modules.View(modules.Presenter).run()
if __name__ == "__main__":
main()
/modules/__init__.py
from modules.Model.Model import Model
from modules.Model.progressbar import ProgressBar
from modules.View.View import View
from modules.Presenter.Presenter import Presenter
/modules/Model/Model.py
class Model:
def __init__(self):
pass
def long_process(self):
import time
for i in range(10):
time.sleep(0.1)
print("Update the progress bar.")
return True
/modules/Model/progressbar.py
# MIT license: https://gist.github.com/vladignatyev/06860ec2040cb497f0f3
import sys
class ProgressBar:
def progress(count, total, status=''):
bar_len = 60
filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1)
bar = '=' * filled_len + '-' * (bar_len - filled_len)
sys.stdout.write('[%s] %s%s ...%s\r' % (bar, percents, '%', status))
sys.stdout.flush()
/modules/View/View.py
import sys
class View:
def __init__(self, presenter):
self.presenter = presenter(self)
def run(self):
self.presenter.long_process()
def update_progress_bar(self, msg):
sys.stdout.write(msg)
def hide_progress_bar(self, msg):
sys.stdout.write(msg)
def update_status(self, msg):
print(msg)
/modules/Presenter/Presenter.py
class Presenter:
def __init__(self, view):
import modules
self.model = modules.Model()
self.view = view
def long_process(self):
if self.model.long_process():
self.view.update_status('Long process finished correctly')
else:
self.view.update_status('error')
def update_progress_bar(self, i, total):
from modules import ProgressBar
ProgressBar.progress(i, total)
self.view.update_progress_bar(ProgressBar.progress(i, total))
def end_progress_bar(self):
self.view.end_progress_bar('\n')
I could do:
class Model:
def __init__(self, presenter):
self.presenter = presenter # Violation of MVP
def long_process(self):
import time
for i in range(10):
time.sleep(0.1)
self.presenter.update_progress_bar(i, 10) # Violation of MVP
print("Update the progress bar.")
return True
But this is wrong since the Model now instantiates the Presenter. Any suggestions?
Use a callback:
import time
class Model:
def long_process(self, notify=lambda current, total: None):
for i in range(10):
time.sleep(0.1)
notify(i, 10)
return True
class Presenter:
def long_process(self):
result = self.model.long_process(lambda c, t: self.update_progress_bar(c, t)):
if result:
self.view.update_status('Long process finished correctly')
else:
self.view.update_status('error')
This keeps your model independant from the client code, while still allowing it (the model I mean) to notify it's caller.
As a side note, there are quite a few things in your code that are totally unpythonic:
1/ you don't have to put each class in a distinct module (it's actually considered an antipattern in Python), and even less in nested submodules (Python Zen: "flat is better than nested").
2/ you don't have to use classes when a plain function is enough (hint: Python functions are objects... actually, everything in Python is an object) - your ProgressBar class has no state and only one method, so it could just be a plain function (Python Zen: "simple is better than complex").
3/ imports should be at the top of the module, not in functions (if you have to put them in a function to solve cyclic dependancies issues then the proper solution is to rethink your design to avoid cyclic dependancies).
4/ module names should be all_lower

Python: Correct use of Click with __main__ and __init__

I have been playing around with the Click package and I can't get the following snippit of code to work properly.
import numpy as np
import click
#click.command()
#click.option('--get_print', default = True)
class CustomClass():
def __init__(s, get_print):
s.data = np.random.normal(0, 1, 10)
s.get_print = get_print
if s.get_print:
print("get_print =", s.get_print)
def print_mean(s):
print("Printing mean:")
mean_ = np.mean(s.data)
if s.get_print:
print(mean_)
if __name__ == '__main__':
CustomClass().print_mean()
It seems that the print_mean function never gets called. If I comment out the #Click decorator, and manually set get_print=True, the print_mean method gets called just fine.
My suspicion is that #Click somehow sets __main__ and so when I do if __name__ == '__main__' it just calls Click again - but perhaps I'm wrong?
In either case, what is the correct way of using Click, when you want the __init__ method to inherit all the options, and if __name__ == '__main__'to actually be able to call functions inside the class, where the __init__ is defined?
This is just to illustrate what I mean in my comments. Decorate a function with the click functions and have that function instantiate your class and execute whatever member function you need. This is what the click docs suggests:
import numpy as np
import click
class CustomClass:
def __init__(self, get_print):
self.data = np.random.normal(0, 1, 10)
self.get_print = get_print
if self.get_print:
print("get_print =", self.get_print)
def print_mean(self):
print("Printing mean:")
mean_ = np.mean(self.data)
if self.get_print:
print(mean_)
#click.command()
#click.option('--get_print', default=True)
def cli(get_print):
cc = CustomClass(get_print)
cc.print_mean()
if __name__ == '__main__':
cli()
...and you probably want get_print to be a flag with default False:
#click.command()
#click.option('--get_print', default=False, is_flag=True, required=False)
def cli(get_print):
cc = CustomClass(get_print)
cc.print_mean()

How can I change the datetime in my Python Atom-class, or create a setter for it?

Hi I'm new to GUI programming, so after trying PyQt for a short time I found Enaml which made the production much more easy.
I'm trying to have a widget that can change the value of a datetime.datetime object or a datetime.time object, but it turns out they are read-only. So how could I do that? Can I change the variable to have read-and-write attributes, or maybe need a setter?
My minimum working solution is:
from __future__ import print_function
import datetime
import os
from atom.api import Atom, Unicode, Range, Typed, observe, Value, Bool
class SimulationCase(Atom):
startDateTime = datetime.datetime.strptime('05.03.2015-5','%d.%m.%Y-%H')
currentDateTime = startDateTime
endDateTime = startDateTime+ datetime.timedelta(days=int(5))
incrementTime = datetime.time(1,0,0)
def main():
case = SimulationCase()
print(case.currentDateTime)
a = datetime.time(1,0,0)
print(a)
#This is where the problem occures, comment out the line under for a working solution.
case.incrementTime = a
if __name__ == '__main__':
main()
Your solution is incorrect. You use the member types to create members in your Atom class which can then be accessed and set through the normal __getattr__ mechanism.
A correct example of your sample code:
from __future__ import print_function
import datetime
import os
from atom.api import Atom, Unicode, Range, Typed, observe, Value, Bool
def default_time_factory():
return datetime.datetime.strptime('05.03.2015-5','%d.%m.%Y-%H')
class SimulationCase(Atom):
startDateTime = Typed( datetime.datetime, factory=default_time_factory )
currentDateTime = Typed( datetime.datetime, factory=default_time_factory )
endDateTime = Typed( datetime.datetime, default=default_time_factory()+datetime.timedelta(days=int5) )
incrementTime = Typed( datetime.time, default=datetime.time(1,0,0) )
def main():
case = SimulationCase()
print(case.currentDateTime)
a = datetime.time(1,0,0)
print(a)
#This now works
case.incrementTime = a
if __name__ == '__main__':
main()
I found a solution based on looking into the Property.py documentation of Nucleic Development Team Atom.
The setter could be done by adding a function with _set_variable(self,variable) and _get_variable(self):
A possible solution is therefore:
from __future__ import print_function
import datetime
import os
from atom.api import Atom, Unicode, Range, Typed, observe, Value, Bool, Property
class SimulationCase(Atom):
startDateTime = Property()
_startDateTime = Typed(datetime.datetime)
currentDateTime = Property()
_currentDateTime = Typed(datetime.datetime)
endDateTime = Property()
_endDateTime = Typed(datetime.datetime)
incrementTime = Property()
_incrementTime = Typed(datetime.time)
# Getter and setter for startDateTime
def _set_startDateTime(self,startDateTime):
self._startDateTime = startDateTime
def _get_startDateTime(self):
return self._startDateTime
# Getter and setter for currentDateTime
def _set_currentDateTime(self,currentDateTime):
self._currentDateTime = currentDateTime
def _get_currentDateTime(self):
return self._currentDateTime
# Getter and setter for endDateTime
def _set_endDateTime(self,endDateTime):
self._endDateTime = endDateTime
def _get_endDateTime(self):
return self._endDateTime
# Getter and setter for incrementTime
def _set_incrementTime(self,incrementTime):
self._incrementTime = incrementTime
def _get_incrementTime(self):
return self._incrementTime
# Populating the data
def __init__(self):
self._startDateTime = datetime.datetime.strptime('05.03.2015-5','%d.%m.%Y-%H')
self._currentDateTime = self._startDateTime
self._endDateTime = self._startDateTime + datetime.timedelta(days=int(5))
self._incrementTime = datetime.time(1,0,0)
def main():
case = SimulationCase()
print(case.currentDateTime)
print(case.incrementTime)
print(case.endDateTime)
a = datetime.time(2,0,0)
case.incrementTime = a
print(case.incrementTime)
if __name__ == '__main__':
main()

Return value From Fucntion to Traits GUI

I am trying to develop a GUI using Enthought. I'm a little confused as to how to use Traits and return a value from a function to be displayed on the GUI. Below I wrote a simple GUI displaying my problems.
from traits.api import HasTraits, Instance, String, Float, Enum, Button, Str, List, Bool, Int
from traitsui.api import Handler, View, Item, Group, HSplit, NoButtons, VGroup, VGrid, HGroup, EnumEditor, Action, ProgressEditor, ButtonEditor
from multiprocessing.pool import ThreadPool
import simple_GUI_function
class Input_Panel(HasTraits):
Name = Str(name='Name', label="Name")
Iterations = Str(name='Iterations',label="Iterations")
#~ #Create the User Information panel
User_Information_Panel = View(VGroup(
VGrid(
Item('Name'),
Item('Iterations'),
),
show_border=True, label="Information"
)
)
class Output_Panel(HasTraits):
counter_out = Int(0)
User_Output_Panel = View(VGrid(
Item('counter_out', width=-10, style='readonly',resizable=False
),
)
)
class Program_Execute_Thread(Handler):
wants_abort = False
pool = ThreadPool(processes=1)
def process(self, iterations):
try:
if self.processing_job.isAlive():
return
except AttributeError:
pass
self.processing_job = Thread(target = process, args = (iterations))
self.processing_job.start()
def run(self):
while not self.wants_abort:
print("here")
async_result = pool.apply_async(simple_GUI_function.Gui_func.Func_test(simple_GUI_function.Gui_func(), iterations))
return_val = async_result.get()
class Start_Panel(HasTraits):
Program_Execute = Instance(Program_Execute_Thread)
Start_Button = Button(name = 'Start', action='_Start_Button', tooltip = 'Start')
Quit_Button = Button(name = 'Exit Program', action='_Quit_Button', tooltip = 'Quit')
Button_Panel = View(HGroup(
Item('Start_Button', show_label = False),
Item('Quit_Button', show_label = False),
)
)
def _Start_Button_fired(self):
if self.Program_Execute and self.Program_Execute.isAlive():
self.Program_Execute.wants_abort = True
else:
self.Program_Execute = Program_Execute_Thread()
self.Program_Execute.start()
print("Start Button pushed")
def _Quit_Button_fired(self):
print("Quit Button pushed")
class MainWindowHandler(Handler):
def close(self, info, is_OK):
#~ if (info.object.button_panel.Program_Execute_Thread and \
#~ info.object.button_panel.Program_Execute_Thread.isAlive()):
#~ info.object.button_panel.Program_Execute_Thread.wants_abort = True
#~ while info.object.Program_Execute_Thread.isAlive():
#~ sleep(0.1)
#~ GUI.process_events()
return True
class MainWindow(HasTraits):
user_input = Instance(Input_Panel, ())
user_output = Instance(Output_Panel, ())
button_panel = Instance(Start_Panel, ())
view = View(VGroup(Item('user_input', style = 'custom', show_label = False),
Item('user_output', style = 'custom', show_label = False),
Item('button_panel', style = 'custom', show_label = False),
show_labels = False),
resizable = True, handler = MainWindowHandler(),
)
if __name__ == '__main__':
MainWindow().configure_traits()
Here is the simple function the code is trying to call:
class Gui_func:
def Func_test(self, iteration):
import time
print("Inside the function")
time.sleep(2)
print("Now leaving the function")
p = iteration + 1
return p
The code will register the Start button begin pushed but keeps generating the error "Program_Execute_Thread" object has no attribute 'start'. I looked at the Camera.py threading example built using Traits and built my code around that example. It seems like Python is not recognizing my Program_Execute_Thread as a "Thread." Anybody have any ideas?
Cheers,
Shivels
Solved my own problem. Looks like I forgot to declare Program_Execute_Thread as "Program_Execute_Thread(Thread)" and also I had to start the thread.

PyQt4 QDialog connections not being made

I am working on an application using PyQt4 and the designer it provides. I have a main window application that works fine, but I wanted to create custom message dialogs. I designed a dialog and set up some custom signal/slot connections in the __init__ method and wrote an if __name__=='__main__': and had a test. The custom slots work fine. However, when I create an instance of my dialog from my main window application, none of the buttons work. Here is my dialog:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
import encode_dialog_ui
# Ui_EncodeDialog is the python class generated by pyuic4 from the Designer
class EncodeDialog(encode_dialog_ui.Ui_EncodeDialog):
def __init__(self, parent, in_org_im, txt_file, in_enc_im):
self.qd = QDialog(parent)
self.setupUi(self.qd)
self.qd.show()
self.message = (txt_file.split("/")[-1] + " encoded into " +
in_org_im.split("/")[-1] + " and written to " +
in_enc_im.split("/")[-1] + ".")
QObject.connect(self.view_image_button, SIGNAL("clicked()"),
self.on_view_image_button_press)
self.org_im = in_org_im
self.enc_im = in_enc_im
self.encoded_label.setText(self.message)
def on_view_image_button_press(self):
print "hello world"
if __name__ == '__main__':
app = QApplication(sys.argv)
tmp = QMainWindow()
myg = EncodeDialog(tmp,'flower2.png','b','flower.png')
app.exec_()
If I run this class it works fine, and pressing the view_image_button prints hello world to the console. However when I use the call
#self.mw is a QMainWindow, the rest are strings
EncodeDialog(self.mw, self.encode_image_filename,
self.encode_txt_filename,
self.encode_new_image_filename)
in my main window class, the dialog displays correctly but the view_image_button does nothing when clicked. I have googled for a solution, but couldn't find anything useful. Let me know if you need any more information. Any help on this would be appreciated!
As requested below is some more code from my main window class for brevity's sake I have added ellipses to remove code that seemed irrelevant. If no one can think of anything still, I will add more. (If indenting is a little off, it happened in copy-pasting. The orignal code is correct)
class MyGUI(MainWindow.Ui_MainWindow):
def __init__(self):
self.mw = QMainWindow()
self.setupUi(self.mw)
self.mw.show()
self.encode_red_bits = 1
self.encode_blue_bits = 1
self.encode_green_bits = 1
self.decode_red_bits = 1
self.decode_blue_bits = 1
self.decode_green_bits = 1
self.encode_image_filename = ""
self.encode_new_image_filename = ""
self.encode_txt_filename = ""
self.decode_image_filename = ""
self.decode_txt_filename = ""
# Encode events
...
QObject.connect(self.encode_button, SIGNAL("clicked()"),
self.on_encode_button_press)
# Decode events
...
# Encode event handlers
...
def on_encode_button_press(self):
tmp = QErrorMessage(self.mw)
if (self.encode_image_filename != "" and
self.encode_new_image_filename != "" and
self.encode_txt_filename != ""):
try:
im = Steganography.encode(self.encode_image_filename, self.encode_txt_filename,
self.encode_red_bits, self.encode_green_bits,
self.encode_blue_bits)
im.save(self.encode_new_image_filename)
encode_dialog.EncodeDialog(self.mw, self.encode_image_filename,
self.encode_txt_filename,
self.encode_new_image_filename)
except Steganography.FileTooLargeException:
tmp.showMessage(self.encode_txt_filename.split("/")[-1] +
" is to large to be encoded into " +
self.encode_image_filename.split("/")[-1])
else:
tmp.showMessage("Please specify all filenames.")
# Decode event handlers
...
if __name__ == '__main__':
app = QApplication(sys.argv)
myg = MyGUI()
app.exec_()
It feels like the signal is just not getting passed from the parent down to your child QDIalog.
Try these suggestions:
Use the new method for connecting signals
Instead of extending the classes pyuic created, extend the actual QT classes and call the ones generated by pyuic
Your new code will look something like this:
class MyGUI(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.mw = MainWindow.Ui_MainWindow()
self.mw.setupUi(self)
self.mw.show()
...
self.encode_button.clicked.connect(self.on_encode_button_press)
...
class EncodeDialog(QDialog):
def __init__(self, parent, in_org_im, txt_file, in_enc_im):
QDialog.__init__(self, parent)
self.qd = encode_dialog_ui.Ui_EncodeDialog()
self.qd.setupUi(self)
self.qd.show()
...
self.view_image_button.clicked.connect(self.on_view_image_button_press)
...

Categories

Resources