How to get groups and roles of a QPalette in PyQt/PySide? - pyqt

Instead of "manually" defining lists groups and roles (in my code below), how can I query the PyQt/PySide application for these values?
from PyQt4 import QtGui
groups = ['Disabled', 'Active', 'Inactive', 'Normal']
roles = [
'AlternateBase',
'Background',
'Base',
'Button',
'ButtonText',
'BrightText',
'Dark',
'Foreground',
'Highlight',
'HighlightedText',
'Light',
'Link',
'LinkVisited',
'Mid',
'Midlight',
'Shadow',
'ToolTipBase',
'ToolTipText',
'Text',
'Window',
'WindowText'
]
def getPaletteInfo():
palette = QtGui.QApplication.palette()
#build a dict with all the colors
result = {}
for role in roles:
print role
for group in groups:
qGrp = getattr(QtGui.QPalette, group)
qRl = getattr(QtGui.QPalette, role)
result['%s:%s' % (role, group)] = palette.color(qGrp, qRl).rgba()
return result

This can be done with standard python introspection techniques:
for name in dir(QtGui.QPalette):
if isinstance(getattr(QtGui.QPalette, name), QtGui.QPalette.ColorGroup):
print(name)
and the same can be done with QtGui.QPalette.ColorRole.
But note that this will produce a few extra items that you might not be expecting. There are NColorGroups and NColorRoles. which give the number of items in each enum; there are a few synonyms, such as Window/Background; and one or two others, such as All and NoRole.

Related

st_aggrid nested grids don't display changed data after DF is altered

Using ...
streamlit==1.9.0
streamlit-aggrid==0.2.3.post2
Python 3
I'm attempting to use nested AgGrid Grids as described here
The end goal is to show a list of instruments and, (only) when checked, the subgrid below the instrument updates with data about the instrument.
The problem is; the updated Grid (updated after selecting a checkbox, which alters the underlying DataFrame) does not actually show the new DF data (it still shows the old data.)
DETAILS:
When I first load the page, it creates a DataFrame st.session_state["instruments_and_injections_df"] correctly using build_initial_instruments_df()
The Grid appears correctly, with the nested data, and checkboxes next to each line in both the outer grid, and the nested grid.
Selecting a checkbox does trigger _get_injection(instrument_name) and _update_instruments_and_injections_df(...)
These functions have been validated as working (it does query the database and update the DF with the new data.) If I dump the updated DF st.session_state["instruments_and_injections_df"] we can see the JSON data has been correctly added to the instrumentData column on the correct row.
After selecting a checkbox, however, the data in the main grid does not change (still shows the "instrumentData": {"sample_name":"test"}, not the new data)
Selecting a second checkbox, also queries the database, and updates the Df - but then the grid is completely goofy.
I have tried changing the update_mode in AgGrid() call to "MANUAL", "VALUE_CHANGED", "SELECTION_CHANGED", and "MODEL_CHANGED". The behavior does not change.
def build_initial_instruments_df():
""" This works to build initial grid. Grid behaves correctly at this point"""
st.session_state["raw_json"] = json.dumps([
{ "instrument_name":"Inst1", "instrumentData": {"sample_name":"test"},
{ "instrument_name":"Inst2", "instrumentData": {"sample_name":"test"}
])
st.session_state["instruments_and_injections_df"] = pd.read_json(st.session_state["raw_json"])
st.session_state["instruments_and_injections_df"]["instrumentData"] = st.session_state["instruments_and_injections_df"]["instrumentData"].apply(lambda x: pd.json_normalize(x))
def _update_instruments_and_injections_df(selected = [], always_update = False):
""" This works to query database, and update the field "instrumentData" with JSON.
Verified by dumping updated st.session_state["instruments_and_injections_df"] to screen which shows the updated JSON cell"""
_instruments = st.session_state["instruments_and_injections_df"]["instrument_name"].to_list()
for _instrument in _instruments:
if _instrument in selected:
_injection_json = _get_injection(_instrument)
_instrument_index = st.session_state["instruments_and_injections_df"].index[st.session_state["instruments_and_injections_df"]['instrument_name']==_instrument].tolist()[0]
st.session_state["instruments_and_injections_df"].at[_instrument_index, "instrumentData"] = _injection_json
def _get_injection(instrument_name):
""" This works to obtain the data from the DB and convert it to JSON"""
with st.spinner("Displaying results..."):
_sql = "SELECT sample_name, sample_orderno, sample_amount FROM injections WHERE instrument_name = '{}';".format(instrument_name)
injection_df = pd.read_sql(_sql, st.session_state["conn"])
injection_df_json = injection_df.to_json(orient = "records")
injection_df_json = json.loads(injection_df_json)
return injection_df_json
#===============================================================================
# MAIN
#===============================================================================
try:
st.session_state["instruments_and_injections_df"] # Will error if has never been loaded
except (KeyError, NameError):
build_initial_instruments_df()
gridOptions = {
# enable Master / Detail
"masterDetail": True,
"rowSelection": "multiple",
"pagination": st.session_state["enable_pagination"],
"paginationAutoPageSize": st.session_state["paginationAutoSize"],
"groupSelectsChildren":st.session_state["groupSelectsChildren"],
"groupSelectsFiltered":st.session_state["groupSelectsFiltered"],
# the first Column is configured to use agGroupCellRenderer
"columnDefs": [
{
"field": "instrument_name",
"cellRenderer": "agGroupCellRenderer",
"checkboxSelection": True,
},
],
"defaultColDef": {
# "flex": 1,
},
# provide Detail Cell Renderer Params
"detailCellRendererParams": {
# provide the Grid Options to use on the Detail Grid
"detailGridOptions": {
"rowSelection": "multiple",
"suppressRowClickSelection": True,
"enableRangeSelection": True,
"pagination": st.session_state["enable_pagination"],
"paginationAutoPageSize": st.session_state["paginationAutoSize"],
"groupSelectsChildren":st.session_state["groupSelectsChildren"],
"groupSelectsFiltered":st.session_state["groupSelectsFiltered"],
"columnDefs": [
{"field": "sample_name", "checkboxSelection": True},
],
"defaultColDef": {
"sortable": True,
# "flex": 1, # This sets the columns to fit to the window (bad)
},
},
"getDetailRowData": JsCode(
"""function (params) {
console.log(params);
params.successCallback(JSON.parse(params.data.instrumentData));
}"""
).js_code,
},
}
st.session_state["instruments_grid_response"] = AgGrid(
st.session_state["instruments_and_injections_df"],
gridOptions=gridOptions,
height=st.session_state["grid_height"],
allow_unsafe_jscode=True,
enable_enterprise_modules=True,
# update_mode=GridUpdateMode.SELECTION_CHANGED,
data_return_mode=st.session_state["return_mode_value"],
update_mode=st.session_state["update_mode_value"],
)
#===============================================================================
# Discover any data selected from grid
#===============================================================================
if st.session_state["instruments_grid_response"]['selected_rows'] != []:
_selected_instruments = pd.DataFrame(st.session_state["instruments_grid_response"]['selected_rows'])["instrument_name"].to_list()
_update_instruments_and_injections_df(selected = _selected_instruments)
else:
st.write("No instruments selected")

SqlAlchemy dynamic loading of related entities from query

The use case is pretty simple: I have 3 cascading entities
class Customer():
users = relationship(
'User',
backref=backref('customer', lazy="subquery"),
cascade=DbConstants.RELATIONSHIP_CASCADE_ALL_DELETE)
class User():
reports = relationship('Report',
backref=backref('user', lazy="subquery"),
lazy="subquery",
cascade=DbConstants.RELATIONSHIP_CASCADE_ALL_DELETE)
class Report():
date_time_start = Column(DateTime())
date_time_end = Column(DateTime())
I want to get all these entities in one query, but i want to filter the reports by their date.
customers = session.query(Customer).join(
User, Customer.users, isouter=True
).join(
Report,
# this is where the reports should be filtered
and_(Report.user_id == User.id, Report.date_time_start > date_start, Report.date_time_start < date_end),
).all()
From this I get the expected entity tree:
[
customers:
users: [ reports: [] ]
]
Except i get ALL the reports of the user in the array, no matter the start date.
This means if I check the result like customers[0].users[0].reports, all the reports belonging to this user will be output. Is there a way so the reports attribute is only populated with the rows from the query ?

Python Storing Dynamic Command inside Dynamic Variable

I've got this list:
appliances = [{'firewall': 'Washington', 'firewall_endpoint': '192.168.1.254:443'}, {'firewall': 'Atlanta', 'firewall_endpoint': '10.8.6.1:6565'}]
This list can be dynamic as appliances are added to the network. I found some code that creates dynamic variables based on the item in the list:
for num in range(len(appliances)):
exec(f'firewall_endpoint_'+str(num)+'_session = firewallhost[num]')
I'm able to print out a list of current variables and you can see it creates the two firewall_endpoint_x variables:
['firewall_endpoint_0', 'firewall_endpoint_1', 'time', 'requests', 'sys', 'pprint', 'json', 'socket', 'struct', 'ipaddress', 'InsecureRequestWarning', 'username', 'password', 'FGT', 'appliances', 'num']
Here is an example of how to login to one session:
fgt=FGT(ipaddress:port)
fgt.login(name=username, key=password)
I'm trying to login to both of them at the same time so both of them have their own session. The syntax is all messed up. I have no idea what the right syntax is
username = admin
password = password123
class FGT(object):
def login(self, name, key):
url = self.url_prefix + '/logincheck'
res = self.session.post(url,
data='username=' + name + '&secretkey=' + key,
verify = False)
if type(appliances) == list:
## Create dynamic list of firewall variables and login
for num in range(len(firewallhost)):
exec(f'firewall_endpoint_'+str(num)+'_session = FGT(firewallhost[num][firewall_endpoint]).login(username, password)')

How do i send the role name?

if before.roles != after.roles:
with open("log.json", 'r') as f:
ac = json.load(f)
for key in ac:
if int(key) == after.guild.id:
# for key in ac:
channel = client.get_channel(ac[key])
#embed = discord.Embed(
# colour = COLOR
#)
#embed.set_author(name=NAME)
#embed.set_thumbnail(url=PFP)
#embed.add_field(name="Old nickname", value=f"{before.display_name}", inline=True)
#embed.add_field(name="New nickname", value=f"{after.display_name}", inline=True)
await channel.send(f"{before.roles}, {after.roles}")
#await channel.send(embed=embed)
I just keep getting this.
How would I make it show the role that was removed/added without all the other junk?
If you want to print just all the role names, then you can use the join function and pass in the roles as the iterable.
For example, the below will send before and after roles, where the roles are joined by a comma:
before_roles = ",".join([role.name for role in before.roles])
after_roles = ",".join([role.name for role in after.roles])
await channel.send(f"{before_roles}, {after_roles}")

Python unknown number of commandline arguments in boto3

I am trying to add tags based on commandline arguments passed to python script something like below:
./snapshot-create.py --id abcd --key1 Env --value1 Test
The script is like below:
client = boto3.client('ec2')
response = client.create_tags(
Resources=[
ID,
],
Tags=[
{
'Key': 'key1',
'Value': 'value1'
},
]
)
I want to use --key1 and --values as Tags as above but the problem is that there could be more than one tags that need to be added like:
./snapshot-create.py --id abcd --key1 Env --value1 Test -key2 Loca --value2 US -key1 Size --value1 small ...
How would I use those key-values if their number of arguments is not fixed.
I don't mind using function or any other way than what I came up with.
One option would be loading a json string as a dictionary and iterating it when creating the tags.
For example, consider this invocation:
$ my_script.py --tags "{'tag1': 'value1', 'tag2': 'value2'}" --id i-1234567890 i-0987654321
and this code snippet:
import json
import boto3
import argparse
parser.add_argument('-t', '--tags', type=str)
parser.add_argument('-i', '--id', nargs='+')
args = parser.parse_args()
client = boto3.client('ec2')
def create_tags(key, value, resources, c):
c.create_tags(
Resources=
resources,
,
Tags=[
{
'Key': key,
'Value': value
},
]
)
my_tags = json.loads(args.tags) # {'tag1': 'value1', 'tag2': 'value2'}
resources = args.id # ['i-1234567890', 'i-0987654321']
for k, v in my_tags.items():
create_tags(k, v, resources, client)
This should cause instances i-1234567890 & i-0987654321 to be tagged with both tags tag1 and tag2 described in --tags above.
If you require a more dynamic interface for resources as well, consider adding it to the json as such:
{ 'instance_id': [{'tag_key': 'tag_value'} ... ] ... }
You can the take a single argument --tags which will contain a mapping of resources and tags, instead of the above example where resources is statically mapped to the tags.
Pretty sure there are better, more pythonic, solutions than this though - this is one viable solution.

Resources