Enthought traitsui. Dynamically update a list in the GUI - python

OK. So I have been running into this roadblock where I cant get the GUI to update based on what I am doing. I've searched extensively and tried to read but I am at my wits end almost. The closest Ive gotten is to remove the item from the "myclass.uncorrex_thing" and then run "edit_traits", but that just creates a new GUI over the old one...
Summary:
I am getting a list of filenames from a .csv that has alot of items that change daily. I just want to be able to select the filename from a list on the GUI, press a button that does something to the file and checks that filename off the .csv list, then have the dropdown list on the GUI updated with the updated .csv
Here is my code so far
class My_List(HasTraits):
tracker = RecordKeeping()
uncorrex_items = tracker.get_uncorrected_list() #this creates a list of filenames based on a .csv file
uncorrex_items.insert(0,'Select file')
class DataFrameEditorDemo(HasTraits):
err_correct = PostProcessAutoErrorCorrection() #a separate module for correcting the files
myclass = Instance(My_List)
highlighted_thing = Str
Calc = Button('Run Corrections')
traits_view = View(
Item('Calc', label='correct file'),
Item("highlighted_thing", editor= EnumEditor(name = 'object.myclass.uncorrex_items')),
title="MyEditor"
)
def _Calc_fired(self):
if len(self.highlighted_thing) == 8:
self.err_correct.correct_form(self.highlighted_thing) #this corrects the file selected from the dropdown list
#AND it updates the .csv file so the file should be checked as complete and will not show up when "tracker.get_uncorrected_list()" is run again

OK, for anyone looking at this and wondering, I finally solved my issue. Basically had to create a property class which depends on an event (button press). When the button is pressed the highlighted_thing updates and the function which corrects the form and updates the .csv is run
class DataFrameEditorDemo(HasTraits):
err_correct = PostProcessAutoErrorCorrection() #a separate module for correcting the files
tracker = RecordKeeping() #a separate module for managing the .csv
highlighted_thing = Property(List, depends_on = 'Calc')
test = Str
Calc = Button('Run Corrections')
traits_view = View(
Item('Calc', label='correct file'),
Item("test", editor= EnumEditor(name = 'highlighted_thing')),
title="MyEditor"
)
def _get_highlighted_thing(self):
return tracker.get_uncorrected_list()
def _Calc_fired(self):
if len(self.test) == 8:
self.err_correct.correct_form(self.test)

Related

How do I create a menu that execute .py codes from a python diectory dynamically?

I'm new here , and i have a problem in pyqgis 3.16.
I want to make a menu list that every time you click its run a .py file (fuction runf)
But i want to create by a list of a files in the directory and i need to the code to read every file in the directory that a choose and execute the .py code.
But every time its run the runf go to the final of the list and execute the last .py code for every item in the menu.
I think it is because the code just execute runf when i click , but how i change the file_path every time i click ? for execute the right code for every item in the menu.
here the fuction for create itens of menu and the runf(that execute the .py code)
def initGui(self):
self.menu = QMenu('&Name', self.iface.mainWindow())
self.iface.mainWindow().menuBar().addMenu(self.menu)
self.icon_path='icon.path'
self.dirs = os.listdir(path )
self.total=[]
self.runfile=[]
self.file_name=[]
self.count=0
count1=0
for self.file in self.dirs:
if self.file.endswith(".py"):
self.runfile.append(os.path.join(path, self.file))
basename = os.path.basename(str(self.runfile))
self.file_name.append(os.path.splitext(basename)[0])
self.action = QAction(self.file_name[self.count])
self.action.triggered.connect(self.runf)
self.action.setIcon(QIcon(self.icon_path))
self.total.append(self.action)
self.count=self.count+1
for i in range(len(self.total)):
self.menu.addAction(self.total[i])
def runf(self):
with open(self.runfile[self.count],"r") as rnf:
exec(rnf.read())
PS: sorry by my english
In your runf method, you always open the "last" file with self.runfile[self.count] because when you finally run the runf method, self.count is for each entry the same.
Change your runf method to:
def runf(self, file_number):
with open(self.runfile[file_number], "r") as file:
exec(file.read())
And then call action.triggered.connect using a lambda (but it needs an additional workaround):
def initGui(self):
# ...
for self.file in self.dirs:
if self.file.endswith(".py"):
# ...
self.action.triggered.connect(
lambda file_number=self.count: self.runf(file_number)
)
# ...
BTW there is no need to have self.action, self.count and so on as member variables when you can use local variables. You overwrite them with every iteration in your for-loop.

How to access the variables of a class - Python

I have the following code to enable the file browser using blender:
import bpy
import os
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
sel = ''
class OpenBrowser(bpy.types.Operator):
bl_idname = "open.file"
bl_label = "Select Excel File"
bli_description = "Simulation output excel file"
filter_glob: StringProperty(default = '*.xls;*.xlsx',options = {'HIDDEN'})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
#somewhere to remember the address of the file
def execute(self, context):
global sel
sel = self.filepath
#self.selected_file = self.filepath
#display = "filepath= "+self.filepath
#print(display) #Prints to console
#Window>>>Toggle systen console
return {'FINISHED'}
def invoke(self, context, event): # See comments at end [1]
context.window_manager.fileselect_add(self)
global sel
sel = self.filepath
#Open browser, take reference to 'self'
#read the path to selected file,
#put path in declared string type data structure self.filepath
return {'RUNNING_MODAL'}
# Tells Blender to hang on for the slow user input
bpy.utils.register_class(OpenBrowser)
#Tell Blender this exists and should be used
# [1] In this invoke(self, context, event) is being triggered by the below command
#but in your script you create a button or menu item. When it is clicked
# Blender runs invoke() automatically.
#execute(self,context) prints self.filepath as proof it works.. I hope.
bpy.ops.open.file('INVOKE_DEFAULT')
print(sel)
The issue I am facing is that I have declared a global variable sel to which I want to save the filepath selected from the user when running the code. However, when I run the script I see that sel has not changed and it is as it was initialized. Could someone please help me on how to access from the class the self.filepath variable? What am I doing wrong here?
If I understand correctly, you want to store that value for later.
I'm not sure why 'sel' doesn't even update in your case, but I think the more correct way would be to use a property like so:
import bpy
# Assign a custom property to an existing type.
bpy.types.Scene.my_sel_value = bpy.props.StringProperty(name="Sel")
# Set property value.
bpy.context.scene.my_sel_value = "Foo"
# Get property value.
print(bpy.context.scene.my_sel_value)
Properties can be added to all ID types, but for "global" values, bpy.types.scene us ussualy used. Though there can be multiple scenes in one project and they will have separate values. Property values are stored when Blender closes.
If you are making an addon, you can also store your value in Addon Preferences. This value will be the same for all blender projects.

How to solve PyDeadObjectError when restoring wxribbon GUI state in wxpython

I am building a GUI using wxribbon of wxpython. The wxribbon is dynamic and user can add pages and panels. When user closes the ribbon, I am saving names and number of pages and panels in a json file before destroying the ribbon. On restoring the state, I read from json file and recreate my ribbon state, but when user want to now make changes to ribbon panel, only the last recreated panel works and for all the panels before the last one, I get following error :
**self.Bind(wx.EVT_MENU, lambda event: self.RemoveGroupBox(event, panel), RemoveGroupBox)
File "C:/Users/Samyak/Desktop/Japan_SRC/test/src/GUI/Trial Ribbon.py", line 327, in RemoveGroupBox
for child in newpanel.GetChildren():
File "C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\_core.py", line 14619, in __getattr__
raise PyDeadObjectError(self.attrStr % self._name)
wx._core.PyDeadObjectError: The C++ part of the RibbonPanel object has been deleted, attribute access no longer allowed.**
The code I am using to restore my ribbon state is as follows: Please help me... Thanks a lot
if os.path.exists(CONFIGFILE):
with open(CONFIGFILE, 'r') as f:
data = json.load(f)
self.newpanel = []
for Page in data['pages']:
label = Page['label']
name = Page['name']
newpage = RB.RibbonPage(self._ribbon,wx.ID_ANY, Page['label'],Bitmap("eye.xpm"))
for panels in Page['Panel']:
pagelabel = panels['labelpanel']
self.newpanel.append(RB.RibbonPanel(newpage,wx.ID_ANY,pagelabel,Bitmap("selection_panel.xpm")))
length = len(self.newpanel)
self.newpanel[length-1].Bind(wx.EVT_RIGHT_UP, lambda event: self.RightClickRibbonPageBox(event, self.newpanel[length-1]))
currentpage = data['activepage']
self._ribbon.SetActivePage(currentpage)
self._ribbon.Realize()
I have found the error myself after trying for a long time. I am sorry for not providing the entire code as it was really big. The problem I was having was that I was not checking the condition of
"if isinstance(child, RB.RibbonPanel):".
while recreating ribbon from json file.After using it everything works fine.

get a list of comma separated values in python and use that list to perform a command as many times as necessary with each value

I'm sure this is something easy to do for someone with programming skills (unlike me). I am playing around with the Google Sites API. Basically, I want to be able to batch-create a bunch of pages, instead of having to do them one by one using the slow web form, which is a pain.
I have installed all the necessary files, read the documentation, and successfully ran this sample file. As a matter of fact, this sample file already has the python code for creating a page in Google Sites:
elif choice == 4:
print "\nFetching content feed of '%s'...\n" % self.client.site
feed = self.client.GetContentFeed()
try:
selection = self.GetChoiceSelection(
feed, 'Select a parent to upload to (or hit ENTER for none): ')
except ValueError:
selection = None
page_title = raw_input('Enter a page title: ')
parent = None
if selection is not None:
parent = feed.entry[selection - 1]
new_entry = self.client.CreatePage(
'webpage', page_title, '<b>Your html content</b>',
parent=parent)
if new_entry.GetAlternateLink():
print 'Created. View it at: %s' % new_entry.GetAlternateLink().href
I understand the creation of a page revolves around page_title and new_entry and CreatePage. However, instead of creating one page at a time, I want to create many.
I've done some research, and I gather I need something like
page_titles = input("Enter a list of page titles separated by commas: ").split(",")
to gather a list of page titles (like page1, page2, page3, etc. -- I plan to use a text editor or spreadsheet to generate a long list of comma separated names).
Now I am trying to figure out how to get that string and "feed" it to new_entry so that it creates a separate page for each value in the string. I can't figure out how to do that. Can anyone help, please?
In case it helps, this is what the Google API needs to create a page:
entry = client.CreatePage('webpage', 'New WebPage Title', html='<b>HTML content</b>')
print 'Created. View it at: %s' % entry.GetAlternateLink().href
Thanks.
Whenever you want to "use that list to perform a command as many times as necessary with each value", that's a for loop. (It may be an implicit for loop, e.g., in a map call or a list comprehension, but it's still a loop.)
So, after this:
page_titles = raw_input("Enter a list of page titles separated by commas: ").split(",")
You do this:
for page_title in page_titles:
# All the stuff that has to be done for each single title goes here.
# I'm not entirely clear on what you're doing, but I think that's this part:
parent = None
if selection is not None:
parent = feed.entry[selection - 1]
new_entry = self.client.CreatePage(
'webpage', page_title, '<b>Your html content</b>',
parent=parent)
if new_entry.GetAlternateLink():
print 'Created. View it at: %s' % new_entry.GetAlternateLink().href
And that's usually all there is to it.
You can use a for loop to loop over a dict object and create multiple pages. Here is a little snippet to get you started.
import gdata.sites.client
client = gdata.sites.client.SitesClient(
source=SOURCE_APP_NAME, site=site_name, domain=site_domain)
pages = {"page_1":'<b>Your html content</b>',
"page_2":'<b>Your other html content</b>'}
for title, content in pages.items():
feed = client.GetContentFeed()
parent = None
if selection is not None:
parent = feed.entry[selection - 1]
client.CreatePage('webpage', page_title, content, parent=parent)
as for feeding your script from an external source I would recommend something like a csv file
##if you have a csv file called pages.csv with the following lines
##page_1,<b>Your html content</b>
##page_2,<b>Your other html content</b>
import csv
with open("pages.csv", 'r') as f:
reader = csv.reader(f)
for row in reader:
title, content = row
client.CreatePage('webpage', title, content, parent=parent)

django list not destroyed between views

I am creating a list from some of the model data but I am not doing it correctly, it works but when I refresh the page in the broswer reportResults just gets added to. I hoped it would get garbage collected between requests but obviously I am doing something wrong, any ideas anyone??
Thanks,
Ewan
reportResults = [] #the list that doesn't get collected
def addReportResult(fix,description):
fix.description = description
reportResults.append(fix)
def unitHistory(request,unitid, syear, smonth, sday, shour, fyear, fmonth, fday, fhour, type=None):
waypoints = Fixes.objects.filter(name=(unitid))
waypoints = waypoints.filter(gpstime__range=(awareStartTime, awareEndTime)).order_by('gpstime')[:1000]
if waypoints:
for index in range(len(waypoints)):
...do stuff here selecting some waypoints and generating "description" text
addReportResult(waypointsindex,description) ##append the list with this, adding a text description
return render_to_response('unitHistory.html', {'fixes': reportResults})
You are reusing the same list each time, to fix it you need to restructure your code to create a new list on every request. This can be done in multiple ways and this is one such way:
def addReportResult(reportResults, fix,description):
fix.description = description
reportResults.append(fix)
def unitHistory(request,unitid, syear, smonth, sday, shour, fyear, fmonth, fday, fhour, type=None):
reportResults = [] # Here we create our local list that is recreated each request.
waypoints = Fixes.objects.filter(name=(unitid))
waypoints = waypoints.filter(gpstime__range=(awareStartTime, awareEndTime)).order_by('gpstime')[:1000]
if waypoints:
for index in range(len(waypoints)):
# Do processing
addReportResult(reportResults, waypointsindex, description)
# We pass the list to the function so it can use it.
return render_to_response('unitHistory.html', {'fixes': reportResults})
If the addReportResult stays small you could also inline the description attribute set by removing the call to addReportResult altogether and doing the waypointsindex.description = description at the same position.
Just so you're aware of the life cycle of requests, mod_wsgi will keep a process open to service multiple requests. That process gets recycled every so often but it is definitely not bound to a single request as you've assumed.
That means you need a local list. I would suggest moving the addReportResult function contents directly in-line, but that's not a great idea if it needs to be reusable or if the function is too long. Instead I'd make that function return the item, and you can collect the results locally.
def create_report(fix, description): # I've changed the name to snake_casing
fix.description = description
return fix
def unit_history(request,unitid, syear, smonth, sday, shour, fyear, fmonth, fday, fhour, type=None):
reports = []
waypoints = Fixes.objects.filter(name=(unitid))
waypoints = waypoints.filter(gpstime__range=(awareStartTime, awareEndTime)).order_by('gpstime')[:1000]
if waypoints:
for index in range(len(waypoints)):
report = create_report(waypointsindex, description)
reports.append(report)
return render_to_response('unitHistory.html', {'fixes': reportResults})

Categories

Resources