I need some hints to find a simple solution for inserting a popup window inside a python console app.
This app runs normally unattended, because it's done to be launched from crontab.
It uses everywhere logging to display messages and save them to logfiles.
However, in some cases, the app needs user intervention to choose some options when it is not able to find a suitable one.
That's why I inserted a --interactive option in argparse, and when the app needs user intervention, a popup window in console should appear, allowing the user to choose between some items in a list.
Here's an extract of the output to give you an example :
INFO : Try to fuzzy-match 'Orange Itbn'
INFO : Fuzzy-matched alternative entries : ['Orange Is The New Black']
INFO : Fuzzy matched 'Orange Itbn' as seriesname 'Orange Is The New Black'
INFO : MOVE /Users/spadazz/testing/orange itbn.s03e10.hdtv.720p.mkv TO:
/Volumes/NAS/TV Shows/Orange Is The New Black/S03/Orange Is The New Black.S03E10.hdtv.720p.mkv
INFO : Try to fuzzy-match 'Sur'
INFO : Fuzzy-matched alternative entries : ['Survivors 2008', 'Survivors']
WARNING :
Series 'Sur' not uniquely matched in titles
Choose between these titles :
['Survivors 2008', 'Survivors']
WARNING :
******************************************
**** INSERT HERE THE CALL TO THE POPUP ***
******************************************
Now, I've read some documentation about tkinter, curses and npyscreen but I wasn't able to come up with something simple for this purpose.
I don't wanna mess with the app structure or put the log messages in a main window..
I just wanna a popup that allows me to choose between some options, even with a simple keypress like '1' and '2' etc...
This should be a python solution too, possibly without calling external commands from os.
Any ideas ??
Thanks
With a little help from Nicholas Cole, who wrote npyscreen, I was able to fix this :
import npyscreen as np
class myPop(np.NPSApp):
def setopt(self, title, oList, multi):
self.title = title
self.options = oList
self.multi = multi
self.height = len(self.options)+1
def main(self):
F = np.Popup(name="Choose an option")
if self.multi:
opt = F.add(np.TitleMultiSelect, name=self.title, max_height=self.height, values=self.options, scroll_exit=True)
else:
opt = F.add(np.TitleSelectOne, name=self.title, max_height=self.height, values=self.options, scroll_exit=True)
F.edit()
self._values = opt.get_selected_objects()
self.result = ( self._values if self.multi and len(self._values) > 1 else self._values[0] )
def ChooseOption(title, oList, multi=False):
pop = myPop()
pop.setopt(title, oList, multi)
pop.run()
return pop.result
# Show a popup with radiobuttons to select 1 item from a list
print ChooseOption('choose a single element', ['a','b','c','d'])
# Show a popup with radiobuttons to multi-select items from a list
print ChooseOption('choose multi-elements', ['a','b','c','d'], True)
Hope this helps.
Enrico
Since npyscreen was written to make that kind of thing really simple, I'd use npyscreen. :)
The example code here is almost exactly what you are asking for.
Related
I am working on a small application on ipywidgets that has multiple frames. This means users can click a button, and then the original set of widgets will be closed and a new set of widgets will appear. The function which I am using has a simplified structure as below:
def to_next_page(x):
current_page.close()
display(next_page)
I have a button in current_page that goes to next_page, and vice versa. When I tried to go back from next_page to current_page, the following message appeared instead of the widget:
Tab(children=(VBox(children=(Text(value='', description='Username:'), Password(description='Password:'), Button(description='Login', style=ButtonStyle()), Button(description='Forget Password', style=ButtonStyle()))), VBox(children=(Text(value='', description='Username:'), Password(description='Password:'), Button(description='Login', style=ButtonStyle()), Button(description='Forget Password', style=ButtonStyle()), Button(description='Sign up', style=ButtonStyle())))), _titles={'0': 'Staff', '1': 'Member'})
Is there any way to go back and forth between widget sets? Thank you.
you cant use .close
use this
out = Output()
def to_next_page(x):
out.clear_output()
with out:
display(next_page)
and for displaying everytime use:
with out:
display(anything)
and for closing use:
out.clear_output()
I'm trying to create a tutorial for a library with Streamlit. My overall idea ist to walk through the different functions and classes and explain them together with user based Input, so everything becomes a litte bit more understandable for beginners.
However, I've written 5 Tutorials previously for more experienced users and would like to reuse some of that code by calling it from within my app and to only maintain it once.
Additionally, I'm walking through a lot of functions and classes, example config files e.g. and I'm calling them from a dict.
As Streamlit offers with st.echo an Option to run code and and then display it I've tried this. Also I've tried to use the python inspect Element together with st.write. However, st.echo simply displays the function name, and st.write together with inspect simply displays a string.
display_code = st.radio("Would you like to display the code?", ("Yes", "No"))
if display_code == "Yes":
with st.echo():
example_function_1()
else:
example_function_1()
Basically I'm looking for an option to pass a function and based on user Input simply run it or run it and display the code and comments to it.
So if the user selected "Yes", the Output would be, while also x,y are returned.
def example_function_1():
"""
This is and example functions that is now displayed.
"""
Some Magic
return x, y
And if the user selected No, then only x,y are returned
Here's a modified version of Streamlit's st.echo() that includes a checkbox:
import contextlib
import textwrap
import traceback
import streamlit as st
from streamlit import source_util
#contextlib.contextmanager
def maybe_echo():
if not st.checkbox("Show Code"):
yield
return
code = st.empty()
try:
frame = traceback.extract_stack()[-3]
filename, start_line = frame.filename, frame.lineno
yield
frame = traceback.extract_stack()[-3]
end_line = frame.lineno
lines_to_display = []
with source_util.open_python_file(filename) as source_file:
source_lines = source_file.readlines()
lines_to_display.extend(source_lines[start_line:end_line])
initial_spaces = st._SPACES_RE.match(lines_to_display[0]).end()
for line in source_lines[end_line:]:
indentation = st._SPACES_RE.match(line).end()
# The != 1 is because we want to allow '\n' between sections.
if indentation != 1 and indentation < initial_spaces:
break
lines_to_display.append(line)
lines_to_display = textwrap.dedent("".join(lines_to_display))
code.code(lines_to_display, "python")
except FileNotFoundError as err:
code.warning("Unable to display code. %s" % err)
You can use it exactly as you'd use st.echo. For example:
with maybe_echo():
some_computation = "Hello, world!"
st.write(some_computation)
You can use session state to pass on user input into on-screen actions. A clear example with radio buttons can be found here. Generally speaking, you need to use st.write() to accomplish this. A simplified example with a slider:
import streamlit as st
x = st.slider('Select a value')
st.write(x, 'squared is', x * x)
What you are looking for is not exactly possible, since you have to specify the function within the with st.echo() block. You can see it here:
import inspect
import streamlit as st
radio = st.radio(label="", options=["Yes", "No"])
if radio == "Yes":
with st.echo():
def another_function():
pass
# Print and execute function
another_function()
elif radio == "No":
# another_function is out of scope here..
another_function()
For this task you can use st.code().
import streamlit as st
with open("test.py") as f:
lines_to_display = f.read()
st.code(lines_to_display, "python")
I am trying to enable selecting one single space for use in Revit MEP 2019 by using the GUI and store the selection for further use in scripts. The code is written in pyRevit. The script runs both from the shell and from the addin button, but when entering the selection mode (PickObject method), I am not allowed to select anything at all. I don't get any errors, it's just that nothing is selectable when entering the selection tool in the GUI.
I have commented in the code what I have tried that didn't work.
from Autodesk.Revit import DB,UI
from Autodesk.Revit.DB import BuiltInCategory
from Autodesk.Revit.UI.Selection import ISelectionFilter,ObjectType
# Definitions:
# Define a space selection filter so that only spaces are selectable
class SpaceSelectionFilter(ISelectionFilter):
def AllowElement(element):
#if element.Category.Name == "Spaces":
#if element.ToString() == "Autodesk.Revit.DB.Mechanical.Space":
if element.Category.Id.IntegerValue== int(BuiltInCategory.OST_MEPSpaces):
return True
return False
def AllowReference(reference, point):
return False
# Function that enables using PickObject from the PythonRevitShell
def shell_pickobject():
__window__.Hide()
elementReference = uidoc.Selection.PickObject(UI.Selection.ObjectType.Element,spaceFilter,"Select a space(room)")
__window__.Show()
__window__.TopMost = True
return elementReference
# Procedure:
# Create a selection filter
spaceFilter = SpaceSelectionFilter()
# User picks a space
ref = shell_pickobject()
# The following line works also outside of the shell_pickobject() function when used from the GUI addin-button, but spaces are still not selectable.
# elementReference = uidoc.Selection.PickObject(UI.Selection.ObjectType.Element,spaceFilter,"Select a space(room)")
I don't understand where the problem is, my best guess is inside the filter definition. The help string "Select a space(room)" displays correctly in the bottom left corner, and everything but the viewport turns grey like it should when I am supposed to select something in the view. The mouse turns into some kind of "forbidden" symbol.
I would very much appreciate some help with this. Thank you in advance to anyone who might wish to help!
You can find examples in pyRevitMEPÂ source code. I also did an article explaining how to use ISelectionFilter : [Revit] ISelectionFilter example using python. Here is one example (running with revitpythonshell) :
from Autodesk.Revit.UI.Selection import ISelectionFilter
class CustomISelectionFilter(ISelectionFilter):
def __init__(self, category_name):
self.category_name = category_name
def AllowElement(self, e):
if e.Category.Name == self.category_name:
return True
else:
return False
def AllowReference(self, ref, point):
return true
try:
ductsel = uidoc.Selection.PickObject(ObjectType.Element,
CustomISelectionFilter("Ducts"),
"Select a Duct")
except Exceptions.OperationCanceledException:
TaskDialog.Show("Operation canceled","Canceled by the user")
__window__.Close()
You can find another example running under pyRevit explained here :Â [pyRevitMEP] ConnectTo : connect MEP elements
I have the "reviewer" field available in my task, and I want to switch the reviewer with the task assignee automatically when the task is moved from the 'In progress' stage to the 'Review' stage. I have the following Python code in my server action:
picture of the code in context
def assignrev(self):
for record in self:
if record['project.task.type.stage_id.name']=='Review':
a=self.res.users.reviewer_id.name
b=self.res.users.user_id.name
record['res.users.user_id.name']=a
record['res.users.reviewer_id.name']=b
and below are links to pictures of my automated action settings:
Server action to run
"When to run" settings
Unfortunately, changing the task stage to 'Review' does not give the expected results. Any suggestion please?
Kazu
Ok I finally got the answer to this. below is a picture of the code in context for Odoo 10:
No "def" of "for record" needed: the code will not run.
I just hope this will be helpful to someone else...
Kazu
My guess is that you are incorrectly calling the fields you're trying to get.
# Instead of this
a = self.res.users.reviewer_id.name
b = self.res.users.user_id.name
record['res.users.user_id.name']=a
record['res.users.reviewer_id.name']=b
# Try this
# You don't need to update the name, you need to update the database ID reference
record['user_id'] = record.reviewer_id.id
record['reviewer_id'] = record.user_id.id
Furthermore, why don't you try using an onchange method instead?
#api.multi
def onchange_state(self):
for record in self:
if record.stage_id.name == 'Review':
record.update({
'user_id': record.reviewer_id.id,
'reviewer_id': record.user_id.id,
})
If you're still having problems, you can use ipdb to debug your code more easily by triggering set_trace in your method.
def assignrev(self):
# Triggers a break in code so that you can debug
import ipdb; ipdb.set_trace()
for record in self:
# Test line by line with the terminal to see where your problem is
I want to launch a warning using tkMessageBox in Python3. This warning is supposed to launch when a user doesn't select an element from a listbox. Unfortunately whenever I try to implement message box it does not launch like it is supposed to. I have code for a script called pietalkgui.py which contains the code where I want to implement the message box:
from tkinter import messagebox
# Gives warning if no user is selected for whisper
def whisperwarning(self):
# show warning to user
showwarning("Select User","Select a user to whisper to!")
# Handles whisper
def whispermessage(self):
# stores element selected in temp variable
temp = self.userslist.get(self.userslist.curselection())
# if no item is selected from userslist (listbox)
if temp == "":
# launch warning to user if no item is selected
self.whisperwarning()
else:
# retrieves usernames from userslist
username = temp
# storing whisper
outwhisper = ' /w "' + username +'" ' + self.messagebox.get("0.0",END)
# handling whisper
self.handler(outwhisper)
# erase message in message box
self.messagebox.delete("0.0",END)
Am I doing something wrong in the implementation of tkMessageBox? Or am I not properly checking if not item is selected from the listbox?
It appears that you are calling the method showwarning, but haven't defined it or imported it. That is the name of a function the messagebox module, so perhaps you need to change this:
showwarning("Select User","Select a user to whisper to!")
... to this:
messagebox.showwarning("Select User","Select a user to whisper to!")
Also, FWIW, this code is slightly incorrect: self.messagebox.delete("0.0",END) -- text indices start at "1.0", not "0.0".