When the user saves a file I want a check to happen prior to saving. If the check fails then it doesn't save. I got this working with mSceneMessage and kBeforeSaveCheck, but I don't know how to customize the pop-up message when it fails. Is this possible?
import maya.OpenMaya as om
import maya.cmds as cmds
def func(retCode, clientData):
objExist = cmds.objExists('pSphere1')
om.MScriptUtil.setBool(retCode, (not objExist) ) # Cancel save if there's pSphere1 in the scene
cb_id = om.MSceneMessage.addCheckCallback(om.MSceneMessage.kBeforeSaveCheck, func)
Right now it displays
File operation cancelled by user supplied callback.
I'm a bit slow to the question, but I needed something similar today so I figured I'd respond. I cannot decide if I would recommend this in the general case, but strictly speaking, it is possible to change a considerable number of static strings in the Maya interface using the displayString command. The easy part is, you know the string you are looking for
import maya.cmds as cmds
message = u"File operation cancelled by user supplied callback."
keys = cmds.displayString("_", q=True, keys=True)
for k in keys:
value = cmds.displayString(k, q=True, value=True)
if value == message:
print("Found matching displayString: {}".format(k))
Running this on Maya 2015 finds over 30000 registered display strings and returns a single matching key: s_TfileIOStrings.rFileOpCancelledByUser. Seems promising to me.
Here's your initial code modified to change the display string:
import maya.OpenMaya as om
import maya.cmds as cmds
def func(retCode, clientData):
"""Cancel save if there is a pSphere1 in the scene"""
objExist = cmds.objExists('pSphere1')
string_key = "s_TfileIOStrings.rFileOpCancelledByUser"
string_default = "File operation cancelled by user supplied callback."
string_error = "There is a pSphere1 node in your scene"
message = string_error if objExist else string_default
cmds.displayString(string_key, replace=True, value=message)
om.MScriptUtil.setBool(retCode, (not objExist))
cb_id = om.MSceneMessage.addCheckCallback(om.MSceneMessage.kBeforeSaveCheck, func)
Related
I'm using BotCity for automation but the system doesn't work.
After listing the image and indicating the action on the UI tab, BotCity does not create the code on the 'CODE' tab, how can I solve this problem?
from botcity.core import DesktopBot
from botcity.maestro import *
class Bot(DesktopBot):
def action(self, execution=None):
self.browse("https://www.facebook.com/groups/xxxxxx")
#in this field, the code should be created after the image is selected, but that doesn't happen, even if I leave the course selected in this line
def not_found(self, label):
print(f"Element not found: {label}")
if __name__ == '__main__':
Bot.main()
I am building a chatbot for table reservation in a hotel.
I have written a custom action to validate and modify the extracted user input. I want to reset my slot and ask the question again if the flag value is False.
Here's my actions.py file:
from typing import Any, Text, Dict, List, Union
from rasa_sdk import Action, Tracker
from rasa_sdk.events import SlotSet, UserUtteranceReverted, EventType, AllSlotsReset
from rasa_sdk.executor import CollectingDispatcher
from word2number import w2n
from .functions import util
class ValidateRestaurantForm(Action):
def name(self) -> Text:
return "user_details_form"
def run(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict) -> List[EventType]:
required_slots = ["number_table", "table_type", "reserve_time"]
for slot_name in required_slots:
if tracker.slots.get(slot_name) is None:
# The slot is not filled yet. Request the user to fill this slot next.
return [SlotSet("requested_slot", slot_name)]
# All slots are filled.
return [SlotSet("requested_slot", None)]
class ActionSubmit(Action):
def name(self) -> Text:
return "action_submit"
def run(
self,
dispatcher,
tracker: Tracker,
domain: "DomainDict",
) -> List[Dict[Text, Any]]:
number_table = tracker.get_slot("number_table")
number_table = w2n.word_to_num(number_table)
table_type = tracker.get_slot("table_type")
reserve_time = tracker.get_slot("reserve_time")
flag, reserve_time_modified = util(reserve_time)
if flag == False:
dispatcher.utter_message(response="utter_deny_reserve_time")
dispatcher.utter_message(response="utter_ask_reserve_time")
return [SlotSet("reserve_time", None)] <-------Resetting the slot and ask
reservation time again
else:
dispatcher.utter_message(response="utter_submit",
number_table=number_table,
table_type=tracker.get_slot("table_type"),
reserve_time=reserve_time_modified)
return [AllSlotsReset()]
I am not able to find any answers on Rasa forums. Please suggest some ideas to solve this problem. I am a beginner in Rasa.
Thanks in advance.
This looks like something that can be done by writing stories that implement branching logic.
You can make flag a slot and have it's value set by ActionSubmit. Then, you can write a story where if after calling ActionSubmit and the flag slot is false, you activate the form.
Update 2021-05-08
This part of ActionSubmit:
flag, reserve_time_modified = util(reserve_time)
could be a separate action named e.g. is_valid_reserve_time that ends with a call to set the flag and reserve_time_modified slot.
Then, you could write a story that branches depending the value of the flag slot.
Alternatively, you could use a form to collect all the required booking information and validate the value users provide to reserve_time as it gets filled. The docs has an example of validating an input that user provides to a form and asking the user to fill again if the input is invalid.
I have been trying to put together a few lines of code that takes a youtube music video URL and converts it to mp3/mp4 + 720p, and then downloads it to my DropBox music folder.
When I'm trying to pass a URL through a TKinter widget (entry bar) I am hitting an error about how the the entry object has no attribute 'type', or that the URL is invalid. Is it something to do with not adding quotation marks to the youtube link or something?
If anyone has any insight I would greatly appreciate it. I assume I'm missing something very obvious, but I can't seem to figure out what exactly.
#!/usr/bin/env python
import sys
import os
import tkinter
from pytube import *
from tkinter import *
top=tkinter.Tk()
yt_variable = StringVar()
def helloCallBack():
#Select youtube link you want to upload and print contents
yt = YouTube(yt_entry)
print(yt.get_videos())
print(yt.filename)
#Set parameters for youtube video
video = yt.get('mp4', '720p')
print(yt.videos)
print("success")
#Select download location for youtube video
video.download('C:/Users/coope/Dropbox/BrokenBow/Music/')
print("Downloaded " + str(yt.filename) + " to " + str(video.download) + " successfully!")
return
yt_label=tkinter.Label(top,text='Paste Link + Press Go')
yt_label.pack()
yt_button=tkinter.Button(top,text='Go',command= helloCallBack)
yt_button.pack()
yt_entry=tkinter.Entry(top, textvariable=yt_variable)
yt_entry.get()
yt_entry.pack()
top.mainloop()
Briefly, you have the following:
yt_entry=tkinter.Entry(top, textvariable=yt_variable)
yt_entry.get()
yt = YouTube(yt_entry)
You are expecting this to create an Entry widget, retrieve its contents, and send that retrieved value to the YouTube() constructor. It does not work like that. You are actually creating an Entry widget, immediately retrieving its contents, throwing away those contents (which will be empty anyway, since by that point you haven't had a chance to put anything into that field), and then attempting to send the Entry widget itself to the YouTube() constructor.
On top of that, you give the Entry widget a textvariable, but then you never use it.
Instead, retrieve the contents of that Entry widget (via its textvariable) within the callback. You can even do it in the YouTube() constructor call.
...
top=tkinter.Tk()
yt_variable = StringVar()
def helloCallBack():
...
yt = YouTube(yt_variable.get())
...
...
yt_entry=tkinter.Entry(top, textvariable=yt_variable)
yt_entry.pack()
top.mainloop()
Since you're not doing anything special with that Entry widget, there's no need for a textvariable at all.
...
top=tkinter.Tk()
def helloCallBack():
...
yt = YouTube(yt_entry.get())
...
...
yt_entry=tkinter.Entry(top,)
yt_entry.pack()
top.mainloop()
Also, there's no need for a bare return at the end of a function. It will return regardless once there's nothing more to do, with a default return value of None (the same as what gets returned with a bare return statement, or with return None).
I use the excellent Python Click library for handling command line options in my tool. Here's a simplified version of my code (full script here):
#click.command(
context_settings = dict( help_option_names = ['-h', '--help'] )
)
#click.argument('analysis_dir',
type = click.Path(exists=True),
nargs = -1,
required = True,
metavar = "<analysis directory>"
)
def mytool(analysis_dir):
""" Do stuff """
if __name__ == "__main__":
mytool()
If someone runs the command without any flags, they get the default click error message:
$ mytool
Usage: mytool [OPTIONS] <analysis directory>
Error: Missing argument "analysis_dir".
This is nice, but I'd quite like to tell (very) novice users that more help is available by using the help flag. In other words, add a custom sentence to the error message when the command is invalid telling people to try mytool --help for more information.
Is there an easy way to do this? I know I could remove the required attribute and handle this logic in the main function, but that feels kind of hacky for such a minor addition.
Message construction for most errors in python-click is handled by the show method of the UsageError class: click.exceptions.UsageError.show.
So, if you redefine this method, you will be able to create your own customized error message. Below is an example of a customization which appends the help menu to any error message which answers this SO question:
def modify_usage_error(main_command):
'''
a method to append the help menu to an usage error
:param main_command: top-level group or command object constructed by click wrapper
:return: None
'''
from click._compat import get_text_stderr
from click.utils import echo
def show(self, file=None):
import sys
if file is None:
file = get_text_stderr()
color = None
if self.ctx is not None:
color = self.ctx.color
echo(self.ctx.get_usage() + '\n', file=file, color=color)
echo('Error: %s\n' % self.format_message(), file=file, color=color)
sys.argv = [sys.argv[0]]
main_command()
click.exceptions.UsageError.show = show
Once you define your main command, you can then run the modifier script:
import click
#click.group()
def cli():
pass
modify_usage_error(cli)
I have not explored whether there are runtime invocations of ClickException other than usage errors. If there are, then you might need to modify your custom error handler to first check that ctx is an attribute before you add the line click.exceptions.ClickException.show = show since it does not appear that ClickException is fed ctx at initialization.
I am searching for a simple dialog with a text entry widget asking the user for some input. The dialog should be easy to run (like the gtk.MessageDialog variants) and as flexible.
There are of course some examples but they are either not flexible enough or too complicated to construct for my taste.
I hate re-inventing the wheel... or a dialog.
Based on an example I found (thanks Ardoris!), I came up with a dialog subclass... hope it helps someone out there!
#!/usr/bin/env python
import gtk
class EntryDialog(gtk.MessageDialog):
def __init__(self, *args, **kwargs):
'''
Creates a new EntryDialog. Takes all the arguments of the usual
MessageDialog constructor plus one optional named argument
"default_value" to specify the initial contents of the entry.
'''
if 'default_value' in kwargs:
default_value = kwargs['default_value']
del kwargs['default_value']
else:
default_value = ''
super(EntryDialog, self).__init__(*args, **kwargs)
entry = gtk.Entry()
entry.set_text(str(default_value))
entry.connect("activate",
lambda ent, dlg, resp: dlg.response(resp),
self, gtk.RESPONSE_OK)
self.vbox.pack_end(entry, True, True, 0)
self.vbox.show_all()
self.entry = entry
def set_value(self, text):
self.entry.set_text(text)
def run(self):
result = super(EntryDialog, self).run()
if result == gtk.RESPONSE_OK:
text = self.entry.get_text()
else:
text = None
return text
The run() method returns either the text entered in the entry box if the user presses <Enter> or clicks Ok. If Cancel is clicked or <Esc> pressed, the run() method returns None.
Except for that, the dialog should behave as any other gtk.MessageDialog instance.
Maybe that is not very general as it assumes you will always have Ok as an option, but that is what I need in 99% of my use cases anyway.
There isn't one available in GTK+. You've got two options:
Create a dialog, pack the Entry and any other content you need (probably the best way in my opinion)
Retrieve the content_area of the MessageDialog and append an Entry to it.
Something along the lines of:
#!/usr/bin/env python
import gtk
messagedialog = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK, message_format="Hello")
action_area = messagedialog.get_content_area()
entry = gtk.Entry()
action_area.pack_start(entry)
messagedialog.show_all()
messagedialog.run()
messagedialog.destroy()
Though it does probably need more refinement to get the Entry to display nicely.
Here's the function I wrote, based on the previous answers here. It's a function instead of a class, which means you can use it in one line.
def get_text(parent, message, default=''):
"""
Display a dialog with a text entry.
Returns the text, or None if canceled.
"""
d = gtk.MessageDialog(parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION,
gtk.BUTTONS_OK_CANCEL,
message)
entry = gtk.Entry()
entry.set_text(default)
entry.show()
d.vbox.pack_end(entry)
entry.connect('activate', lambda _: d.response(gtk.RESPONSE_OK))
d.set_default_response(gtk.RESPONSE_OK)
r = d.run()
text = entry.get_text().decode('utf8')
d.destroy()
if r == gtk.RESPONSE_OK:
return text
else:
return None