npyscreen - addVisibleWhenSelected - Clearing value when hidden - python

I'm quite new to python and I'm trying to put together a little app with npyscreen. Part of this app has a page with a FormControlBox, that reveals a FormControlBox when selected. This second FormControlBox, when selected, reveals some text fields.
The issue I'm having is getting the second FormControlBox to change its value to False if the first one is unselected (making the second hidden). Here is the approach I'm attempting to take in this form class.
class envMenuForm(npyscreen.Form):
def afterEditing(self):
# Updating mapping with values set from form creation
hostConfig["hostname"] = self.hostname.value
hostConfig["domain"] = self.domain.value
hostConfig["fqdn"] = self.fqdn.value
self.parentApp.setNextForm('CEV')
def create(self):
# Defining vars from current baseConfig mapping from JSON file
self.hostname = hostConfig["hostname"]
self.domain = hostConfig["domain"]
self.fqdn = hostConfig["fqdn"]
# Adding text fields with the defaults from the config file
self.hostname = self.add(npyscreen.TitleText, name = "System Hostname:", value = self.hostname)
self.domain = self.add(npyscreen.TitleText, name = "System Domain:", value = self.domain)
self.fqdn = self.add(npyscreen.TitleText, name = "System Fully Qualified Domain Name:", value = self.fqdn)
self.et0status = self.add(npyscreen.FormControlCheckbox, name="Enable Eth0", value = False)
self.et0type = self.add(npyscreen.FormControlCheckbox, name = "Configure as Static Address (ignore for DHCP)", value = False)
self.et0ipaddress = self.add(npyscreen.TitleText, name = "IP Address", value = "127.0.0.1")
self.et0status.addVisibleWhenSelected(self.et0type)
self.et0type.addVisibleWhenSelected(self.et0ipaddress)
def while_editing(self,arg):
if arg is self.et0type:
if arg.hidden:
self.et0type.value = False

After a bunch of refactoring I was able to solve this utilizing the adjust_widgets() with some background logic to ensure it doesn't fire off too much.

Related

Peewee - Recursive CTEs - problem with the documentation example - OperationalError no such column: base.id

I have a problem with Peewee-3 and one of the tutorials in documentation:
http://docs.peewee-orm.com/en/latest/peewee/querying.html#recursive-ctes
When I'm trying to run this code (nearly exact copy from doc) it's rising an error:
Exception has occurred: OperationalError no such column: base.id
Here is my code (there is commented part with some testing categories):
_db = SqliteDatabase(DB_FILE)
class _Base(Model):
class Meta:
database = _db
class Category(_Base):
name = CharField()
parent = ForeignKeyField('self', backref='children', null=True)
# ADDING CATEGORIES
# _db.connect()
# _db.create_tables([Category])
# stocks = Category(name="stocks", parent=None)
# stocks.save()
# models = Category(name="models", parent=None)
# models.save()
# smoke = Category(name="smoke", parent=stocks)
# smoke.save()
# front = Category(name="front", parent=smoke)
# front.save()
# side = Category(name="side", parent=smoke)
# side.save()
# fluffy = Category(name="fluffy", parent=front)
# fluffy.save()
# _db.close()
Base = Category.alias()
level = Value(1).alias('level')
path = Base.name.alias('path')
base_case = (Base
.select(Base.name, Base.parent, level, path)
.where(Base.parent.is_null())
.cte('base', recursive=True))
RTerm = Category.alias()
rlevel = (base_case.c.level + 1).alias('level')
rpath = base_case.c.path.concat('->').concat(RTerm.name).alias('path')
recursive = (RTerm
.select(RTerm.name, RTerm.parent, rlevel, rpath)
.join(base_case, on=(RTerm.parent == base_case.c.id)))
cte = base_case.union_all(recursive)
query = (cte
.select_from(cte.c.name, cte.c.level, cte.c.path)
.order_by(cte.c.path))
for category in query:
print(category.name, category.level, category.path)
What am I doing wrong and how can I fix it, there is an mistake in the documentation?
Thanks for the issue report. The problem is I omitted to select the category "id" in the example from the docs. The fix is quite simple:
base_case = (Base
.select(Base.id, Base.name, Base.parent, level, path) # Add Base.id
.where(Base.parent.is_null())
.cte('base', recursive=True))
...
recursive = (RTerm
.select(RTerm.id, RTerm.name, RTerm.parent, rlevel, rpath) # Add RTerm.id
.join(base_case, on=(RTerm.parent == base_case.c.id)))
I've updated the docs accordingly.

streamlit - Sync input fields

I have two input fields that shall allow user selection via
ID, in the number input
(sorted) name, in the selection box
Changing one input field should update the other to keep them in sync.
How do you implement that behaviour with streamlit?
What I tried so far
ID selected -> update name selection box:
users = [(1, 'Jim'), (2, 'Jim'), (3, 'Jane')]
users.sort(key=lambda user: user[1]) # sort by name
selected_id = st.sidebar.number_input('ID', value=1)
options = ['%s (%d)' % (name, id) for id, name in users]
index = [i for i, user in enumerate(users) if user[0] == selected_id][0]
selected_option = st.sidebar.selectbox('Name', options, index)
Name selected -> update ID number input (using st.empty()):
users = [(1, 'Jim'), (2, 'Jim'), (3, 'Jane')]
users.sort(key=lambda user: user[1]) # sort by name
id_input = st.sidebar.empty()
options = ['%s (%d)' % (name, id) for id, name in users]
selected_option = st.sidebar.selectbox('Name', options)
# e.g. get 2 from "Jim (2)"
id = int(re.match(r'\w+ \((\d+)\)', selected_option).group(1))
selected_id = id_input.number_input('ID', value=id)
To keep the widgets in sync, there are two issues that need to be addressed:
We need to be able to tell when either widget has caused the current selection to change; and
We need to update the state of both widgets at the end of the script so that the browser keeps the new values when the script is re-run for visual updates.
For (1), it looks like there's no way of doing it without introducing some kind of persistent state. Without a way to store the current selection between script runs, we can only compare the two widgets' values with each other and with the default value. This causes problems once the widgets have been changed: For example, if the default value is 1, the value of the number input is 2, and the value from the selectbox is 3, we cannot tell whether it is the number input or the selectbox that was most recently changed (and therefore which one to update to the latest value).
For (2), it's a simple matter of using placeholders and refreshing the widgets whenever the selection has changed. Importantly, the widgets should not be refreshed if the selection has not changed, or we will get DuplicateWidgetID errors (since the content of the widgets will not have changed either, and they will have the same generated keys).
Here's some code that shows one way of dealing with both issues and capturing the user's selection at the end. Note that using #st.cache in this way will persist a single global selection across multiple browser sessions, and will allow anyone to clear the selection via the Streamlit menu -> 'Clear cache', which could be a problem if multiple users are accessing the script at the same time.
import re
import streamlit as st
# Simple persistent state: The dictionary returned by `get_state()` will be
# persistent across browser sessions.
#st.cache(allow_output_mutation=True)
def get_state():
return {}
# The actual creation of the widgets is done in this function.
# Whenever the selection changes, this function is also used to refresh the input
# widgets so that they reflect their new state in the browser when the script is re-run
# to get visual updates.
def display_widgets():
users = [(1, "Jim"), (2, "Jim"), (3, "Jane")]
users.sort(key=lambda user: user[1]) # sort by name
options = ["%s (%d)" % (name, id) for id, name in users]
index = [i for i, user in enumerate(users) if user[0] == state["selection"]][0]
return (
number_placeholder.number_input(
"ID", value=state["selection"], min_value=1, max_value=3,
),
option_placeholder.selectbox("Name", options, index),
)
state = get_state()
# Set to the default selection
if "selection" not in state:
state["selection"] = 1
# Initial layout
number_placeholder = st.sidebar.empty()
option_placeholder = st.sidebar.empty()
# Grab input and detect changes
selected_number, selected_option = display_widgets()
input_changed = False
if selected_number != state["selection"] and not input_changed:
# Number changed
state["selection"] = selected_number
input_changed = True
display_widgets()
selected_option_id = int(re.match(r"\w+ \((\d+)\)", selected_option).group(1))
if selected_option_id != state["selection"] and not input_changed:
# Selectbox changed
state["selection"] = selected_option_id
input_changed = True
display_widgets()
st.write(f"The selected ID was: {state['selection']}")

Confused regarding the usage of -init_ & self in a class

I first wrote the necessary code to get the information I wanted from the internet, and it works. But now I'm trying to make the code look a bit nicer, therefore I want to put it into functions that are in a class. But I'm a bit confused when it comes to the usages of self and _init_. Currently, the code isn't working as I want, meaning it isn't adding the information to my dictionary.
As I have understood, you have to add self as a parameter in every function you create in a class. But I don't think I'm using the _init_ in a correct way.
from bs4 import BeautifulSoup
import requests
# Importing data from Nasdaq
page_link = "https://www.nasdaq.com/symbol/aapl/financials?query=balance-sheet"
page_response = requests.get(page_link, timeout=1000)
page_content = BeautifulSoup(page_response.content, "lxml")
# Creating class that gather essential stock information
class CompanySheet:
# creating dictionary to store stock information
def __init__(self):
self.stockInfo = {
"ticker": "",
"sharePrice": "",
"assets": "",
"liabilities": "",
"shareholderEquity": ""
}
def ticker(self):
# Finding ticker
self.tickerSymbol = page_content.find("div", attrs={"class":"qbreadcrumb"})
self.a_TickerList = self.tickerSymbol.findAll("a")
self.a_TickerList = (self.a_TickerList[2].text)
# Adding ticker to dictionary
self.stockInfo["ticker"] = self.a_TickerList
print(self.a_TickerList)
def share(self):
# Finding share price
self.sharePrice = page_content.findAll("div", attrs={"id":"qwidget_lastsale"})
self.aSharePrice = (self.sharePrice[0].text)
# Transforming share price to desired format
self.aSharePrice = str(self.aSharePrice[1:]).replace( ',' , '' )
self.aSharePrice = float(self.aSharePrice)
# Adding results to dictionary
self.stockInfo["sharePrice"] = self.aSharePrice
"""
def assets(self):
# Finding total assets
totalAssets = page_content.findAll("tr", attrs={"class":"net"})[1]
td_assetList = totalAssets.findAll("td")
tdAssets = (td_assetList[22].text)
# Transforming share price to desired format
tdAssets = str(tdAssets[1:]).replace( ',' , '' )
tdAssets = float(tdAssets)
# Adding results to dictionary
self.stockInfo["assets"] = tdAssets
def liabilites(self):
# Finding total liabilities
totalLiabilities = page_content.findAll("tr", attrs={"class":"net"})[3]
td_liabilityList = totalLiabilities.findAll("td")
tdLiabilities = (td_liabilityList[24].text)
# Transforming share price to desired format
tdLiabilities = str(tdLiabilities[1:]).replace( ',' , '' )
tdLiabilities = float(tdLiabilities)
# Adding results to dictionary
self.stockInfo["liabilities"] = tdLiabilities
def equity(self):
# Finding shareholder equity
netEquity = page_content.findAll("tr", attrs={"class":"net"})[4]
td_equityList = netEquity.findAll("td")
tdShareholderEquity = (td_equityList[24].text)
# Transforming shareholder equity to desired format
tdShareholderEquity = str(tdShareholderEquity[1:]).replace( ',' , '' )
tdShareholderEquity = float(tdShareholderEquity)
# Adding results to dictionary
self.stockInfo["shareholderEquity"] = tdShareholderEquity
"""
companySheet = CompanySheet()
print(companySheet.stockInfo)
All I want the code to do is for each function to parse it's information to my dictionary. I then want to access it outside of the class. Can someone help to clarify how I can use _init_ in this scenario, or do I even have to use it?
init is a constructor, which is called along with the creation of the class object. Whereas, self is an instance of the class, which is used accessing methods and attributes of a python class.
In your code, firstly change:
_init_(self) to __init__(self)
Then, in the methods:
def share(self):
# Finding share price
sharePrice = page_content.findAll("div", attrs={"id":"qwidget_lastsale"})
self.aSharePrice = (sharePrice[0].text)
# Transforming share price to desired format
self.aSharePrice = str(aSharePrice[1:]).replace( ',' , '' )
self.aSharePrice = float(aSharePrice)
# Adding results to dictionary
self.stockInfo["sharePrice"] = self.aSharePrice
Similarly, in all the remaining methods, access the variable through the self keyword.
Now, you also need to call the methods which are updating your dictionary.
So, after you have created the object, call the methods through the object and then print the dictionary, like this:
companySheet = CompanySheet()
companySheet.share()
print(companySheet.stockInfo)
Probably it would work!

How to insert a Libreoffice writer Annotation with a date using Python

Please note, this is a self answered question for reference.
Most of the references to com.sun.star.text.textfield.Annotation refer to Date as being a :: com :: sun :: star :: util :: reference but no end of fiddling about with the contents will actually create an annotation with a date.
Setting Date.Year, Date.Month and Date.Day will appear successful but the annotation itself still appears without a date i.e.
anno = model.createInstance("com.sun.star.text.textfield.Annotation")
anno.Content = "this is my annotation/comment"
anno.Author = doc.DocumentProperties.Author
anno.Date.Year = 2020
anno.Date.Month = 5
anno.Date.Day = 18
The documentation is not always complete, or is not obvious, depending upon where you look.
For LibreOffice 6.0 https://api.libreoffice.org/docs/idl/ref/Annotation_8idl_source.html
The Annotation.idl is described as:
service com::sun::star::text::TextField;
[property]string Author;
[optional, property]string Initials;
[optional, property]string Name;
[property]string Content;
[property]com::sun::star::util::Date Date;
[optional, property]com::sun::star::util::DateTime DateTimeValue;
The key here is the optional DateTimeValue which it would appear is the item that needs to be set to provide the Annotation with a Date and Time.
DateTimeValue structure is from com.sun.star.util.DateTime
To create an annotation (with a date and time) in a writer document using a python script, use the following as a template.
from uno import createUnoStruct
import time
def fs_Annotation(*args):
#get the doc from the scripting context which is made available to all scripts
desktop = XSCRIPTCONTEXT.getDesktop()
model = desktop.getCurrentComponent()
try:
text = model.Text
except:
# The macro has been called externally but LibreOffice was not running at the time
return None
tRange = text.End
cursor = desktop.getCurrentComponent().getCurrentController().getViewCursor()
doc = XSCRIPTCONTEXT.getDocument()
# you cannot insert simple text and text into a table with the same method
# so we have to know if we are in a table or not.
# oTable and oCurCell will be null if we are not in a table
oTable = cursor.TextTable
oCurCell = cursor.Cell
anno = model.createInstance("com.sun.star.text.textfield.Annotation")
anno.Content = "this is my annotation/comment"
#Use documents author
#anno.Author = doc.DocumentProperties.Author
#Use hardcoded text
anno.Author = "Joe Bloggs"
t = time.localtime()
dtv=createUnoStruct("com.sun.star.util.DateTime")
dtv.Year = t.tm_year
dtv.Month = t.tm_mon
dtv.Day = t.tm_mday
dtv.Hours = t.tm_hour
dtv.Minutes= t.tm_min
dtv.Seconds = t.tm_sec
dtv.NanoSeconds = 0
anno.DateTimeValue = dtv
if oCurCell == None: # Inserting into text
text.insertTextContent(cursor, anno, True)
else: # Inserting into a table
oCurCell.insertTextContent(cursor, anno, False)
return None
Section 7.7.2 of Andrew's macro document gives the following, although I have not tested it.
Sub AddNoteAtCursor
Dim vDoc, vViewCursor, oCurs, vTextField
Dim s$
'Lets lie and say that this was added ten days ago!'
Dim aDate As New com.sun.star.util.Date
With aDate
.Day = Day(Now - 10)
.Month = Month(Now - 10)
.Year = Year(Now - 10)
End With
vDoc = ThisComponent
vViewCursor = vDoc.getCurrentController().getViewCursor()
oCurs=vDoc.getText().createTextCursorByRange(vViewCursor.getStart())
s = "com.sun.star.text.TextField.Annotation"
vTextField = vDoc.createInstance(s)
With vTextField
.Author = "AP"
.Content = "It sure is fun to insert notes into my document"
'Ommit the date and it defaults to today!'
.Date = aDate
End With
vDoc.Text.insertTextContent(oCurs, vTextField, False)
End Sub
The API docs contain the same information as the IDL file but are somewhat easier to read.
https://www.openoffice.org/api/docs/common/ref/com/sun/star/text/textfield/Annotation.html

Get file details in python

I'm trying to extract the details of a file using Python. Specifically, when I right click a photo and select properties, under the Details tab of the menu that pops up, there's a whole bunch of details about the file. What I really need is the contents of the "People" detail.
This is the menu in question:
Is there a good way of getting that People detail in a string or something?
Some people have suggested using ExifRead. I tried that, but it didn't pull the People tag out of the Exif data.
This is not EXIF data but rather data that Windows populates for different types of objects in the Windows Property System.
The one you are concerned with is called System.Photo.PeopleNames:
propertyDescription
name = System.Photo.PeopleNames
shellPKey = PKEY_Photo_PeopleNames
formatID = E8309B6E-084C-49B4-B1FC-90A80331B638
propID = 100
searchInfo
inInvertedIndex = true
isColumn = true
isColumnSparse = true
columnIndexType = OnDemand
maxSize = 128
mnemonics = people|people tag|people tags
labelInfo
label = People
sortDescription
invitationText = Add a people tag
hideLabel = false
typeInfo
type = String
groupingRange = Discrete
isInnate = true
canBePurged = true
multipleValues = true
isGroup = false
aggregationType = Union
isTreeProperty = false
isViewable = true
isQueryable (Vista) = false
includeInFullTextQuery (Vista) = false
searchRawValue (Windows 7) = true
conditionType = String
defaultOperation = Equal
aliasInfo
sortByAlias = None
additionalSortByAliases = None
displayInfo
defaultColumnWidth = 11
displayType = String
alignment = Left
relativeDescriptionType = General
defaultSortDirection = Ascending
stringFormat
formatAs = General
booleanFormat
formatAs = YesNo
numberFormat
formatAs = General
formatDurationAs = hh:mm:ss
dateTimeFormat
formatAs = General
formatTimeAs = ShortTime
formatDateAs = ShortDate
enumeratedList
defaultText
useValueForDefault = False
enum
value
text
enumRange
minValue
setValue
text
drawControl
control = Default
editControl
control = Default
filterControl
control = Default
queryControl
control = Default
To access this information in Python, use win32com.propsys.

Categories

Resources