How to automatically handle DialogBoxShowing event in Python(Revit Dynamo)? - revit-api

How do I subscribe to Revit events in Python (Dynamo)?
Specifically DialogBoxShowing so I can see if it is "Export with Temporary Hide/Isolate" warning and select "Leave the Temporary Isolate Mode on and Export"?
It is done in C# here:
http://thebuildingcoder.typepad.com/blog/2013/03/export-wall-parts-individually-to-dxf.html
See sub-heading: Handling and Dismissing a Warning Message
Thanks!

To make it more simple than in the tutorial :
Inside Revit, with RevitPythonShell, the event subscribing part can be very easy.
An event handler is just a callable with two arguments : senderand event. Then the event, or the sender gives parameters to play with, DialogId and OverrideResultin our case.
To keep the Building Coder example, let's go with :
def on_dialog_open(sender, event):
try:
if event.DialogId == 'TaskDialog_Really_Print_Or_Export_Temp_View_Modes':
event.OverrideResult(1002)
# 1001 call TaskDialogResult.CommandLink1
# 1002 call TaskDialogResult.CommandLink2
# int(TaskDialogResult.CommandLink2) to check the result
except Exception as e:
pass #print(e) # uncomment this to debug
You only need to plug this function to an event with the following syntax :
__uiControlledApplication__.DialogBoxShowing += on_dialog_open
This can be done in the startup file of RevitPythonShell :
C:\Users\USERNAME\AppData\Roaming\RevitPythonShell2017\startup.py
(Or in the startup part of your addin)
The better way is to unregister the handler if you don't need it anymore, i.e. when Revit is closing (check the tutorial for more details) :
__uiControlledApplication__.DialogBoxShowing -= on_dialog_open
If you want to try this within the console, you can use :
def on_dialog_open(sender, event):
# [...]
__revit__.DialogBoxShowing += on_dialog_open
And after you try an export:
__revit__.DialogBoxShowing -= on_dialog_open
EDIT : shortcuts for result commands (thanks Callum !)
('Cancel', 2)
('Close', 8)
('CommandLink1', 1001)
('CommandLink2', 1002)
('CommandLink3', 1003)
('CommandLink4', 1004)
('No', 7)
('None', 0)
('Ok', 1)
('Retry', 4)
('Yes', 6)

To answer your first question. Try to read this tutorial from Pierre Moureu : https://github.com/PMoureu/samples-Python-RPS/tree/master/Tutorial-IUpdater.
He his subscribing to a IUpdater.

(sorry not enough reputation to add this as comment to the response by PRMoureu...)
To expand on handling Dialogs a little...
Subscribing to DialogBoxShowing is hugely powerful, we have just rolled out a Dialog Suppressor to silence the ever-frustrating 'Would you like to join walls to the floor you just made' and 'Would you like to attach these walls to the roof'. It can also be used to see what errors users are commonly getting etc.
To investigate a Dialogs message text: event.Message
To reply 'Cancel' to the Dialog: event.OverrideResult(0)
To reply 'Yes' to the Dialog: event.OverrideResult(1)
To reply 'OK' to the Dialog: event.OverrideResult(6)

Related

Python-Telegram-Bot: How to wait for user input after clicking on InlineKeyboardButton

I'm working on a bot with the Python-Telegram-Bot API. Right now I'm updating the bot menu with InlineKeyboardButtons instead of ?menu, ?info type of commands (like on Discord). But I came across some trouble. Let me explain step by step what the command is supposed to do:
Step 1 - User opens up the menu (a commandhandler)
Step 2 - InlineKeyboardButtons load, one of them has an option called "Jokenpo" for the game
Step 3 - I set its callback_data to load a message greeting the player and showing the rules (def jkpMenu).
Step 4 - Then def jkpMenu is supposed to go to the next state, that is def jokenpo. During the function, there's a loop for receiving input like paper, rock, scissors that keeps repeting until user types stop or loses their lives.
varScore = 0
varLives = 5
varTie = 0
JKP = range(2)
...
def menuTest(update: Update, _: CallbackContext) -> int:
commands = [
[InlineKeyboardButton(text='📑 | Changelog', callback_data='clog'), InlineKeyboardButton(text='📑 | Info', callback_data='info')],
[InlineKeyboardButton(text='📑 | Jokenpo', callback_data='jokenpo')]]
update.message.reply_text("Menu:\n", reply_markup=InlineKeyboardMarkup(commands))
def comQuery(update: Update, context: CallbackContext) -> None:
query = update.callback_query
query.answer()
if query.data == 'clog': changelog(update, context)
if query.data == 'info': myInfo(update, context)
if query.data == 'jokenpo': jkpMenu(update, context)
#Jokenpo 1: greetings and rules
def jkpMenu(update: Update, _: CallbackContext) ->JKP:
update.callback_query.message.reply_text('Jokenpo mode. Please type "rock", "paper" or "scissors" to continue')
return JKP
#Jokenpo 2:
def jokenpo(update: Update, _:CallbackContext):
msgUser = update.callback_query.message.text.lower()
global varScore
global varLives
global varTie
while True:
computer = ("rock", "paper", "scissors")
computer = random.choice(computer)
if (msgUser== "rock" and computer == "paper") or (msgUser== "paper" and computer == "scissors") or (msgUser=="scissors" and computer == "rock"):
update.callback_query.message.reply_text("Computer chooses <i>" + computer + "</i>. <b>You lose!</b>", parse_mode ='HTML')
varLives -= 1
if (msgUser== "rock" and computer == "scissors") or (msgUser == "paper" and computer == "rock") or (msgUser == "scissors" and computer == "paper"):
update.callback_query.message.reply_text("Computer chooses <i>" + computer + "</i>. <b>You win!</b>", parse_mode ='HTML')
varScore +=1
if (msgUser== computer):
update.callback_query.message.reply_text("We both chose <i>" + computer +"</i>. <b>It's a tie!</b>", parse_mode ='HTML')
varTie +=1
if (msgUser== "status"):
update.callback_query.message.reply_text("Your current score: " + str(varScore) + " points.\nYou have "+ str(varLives)+ " lives left.\nWe tied " + str(varTie) +' times.')
if (msgUser== "sair") or (varLives == 0):
update.callback_query.message.reply_text("Game finished. You made <b>" + str(varScore) + "</b> points.\nWe tied " + str(varTie) +' times.\n', parse_mode ='HTML')
varScore = 0
varTie = 0
varLives = 5
return ConversationHandler.END
update.callback_query.message.reply_text('Please choose: paper, rock or scissors? ')
return
...
...
def(main):
convHandler = ConversationHandler(
entry_points=[jokenpo],
fallbacks=[],
states = {
JKP: [MessageHandler(Filters.text, jokenpo)]
})
dp.add_handler(convHandler)
Problem is: I managed to do that before, when I used CommandHandlers to access the function and it worked.
But now it only reaches the first function (Step 3), it seems it doesn't return to the state I wanted. When I changed return JKP for return jokenpo(update,_) it did access def jokenpo, but it didn't wait for any answer.
When I set the inline buttons, since they use CallbackContext, I'm confused on how to handle arguments.
I copied only some part of the code, not it complete, since the other parts are related to other functions. Any help is appreciated.
I see several issues in your code snippet & description:
using global variables is in general bad practice. If you want to store user/chat related data, I suggest to use PTBs built-in mechanism for that. see here.
The while True loop in jokenpo doesn't make any sense. In fact, at the end of the body of the loop, you return anyway, so the body is executed exactly once.
As entry points for your conversation you use entry_points = [jokenpo], but jokenpo is a function and not a handler
In the state JKP you have a handler MessageHandler(Filters.text, jokenpo). This means that the update that will be passed to jokenpo will have update.(edited_)message/channel_post, but never update.callback_query - yet you try to access this attribute within jokenpo.
"Then def jkpMenu is supposed to go to the next state, that is def jokenpo" - the callback jkpMenu is not part of your ConversationHandler at all and hence the return value will be ignored completely.
"When I changed return JKP for return jokenpo(update,_) it did access def jokenpo, but it didn't wait for any answer." Sure - you called the function. But waiting for input can only work if the conversationhandler is started, which this doesn't do
"When I set the inline buttons, since they use CallbackContext, I'm confused on how to handle arguments." This one I just don't understand. What do you mean by "arguments" in the context of inline buttons and what does that have to do with CallbackContext?
I have the impression that you need to deepen your understanding of how PTB is supposed to be used, especially ConversationHandler. I suggest to have a look at the introductory example. Following the flow chart & tracking the executed code while running the example should help to get a better understanding of what's going on. Also reading the documentation of ConversationHandler should clarify some things.
Disclaimer: I'm currently the maintainer of python-telegram-bot.
Also for completeness sake: This was already discussed to some extend in the usergroup of PTB, see https://t.me/pythontelegrambotgroup/513856

Python- How to handle error for RTSP link

I've created a python script that checks muliple different urls and ports and detects if there is an RTSP stream on them - it is working fine, but it creates errors when the stream doesn't exist (which I'd obviously expect).
I'm getting [rtsp # 0x16745c0] method DESCRIBE failed: 451 ERROR
What I want to do it add a line to my script so if I get the above error, then I just display it in a message on screen. I've tried the following with no luck:
for x in feedList:
print("[INFO] Checking Link..." + x)
cap=cv2.VideoCapture(x)
try:
# Check if camera opened successfully
if (cap.isOpened()== True):
streamlink = x
print("[INFO] FOUND! Stream Link..." + x)
break
except socket.error:
print("[NO STREAM]" + x)
except:
print("[FAILED]" + x)
pass
The Except cases never get hit, I always just get [rtsp # 0x16745c0] method DESCRIBE failed: 451 ERROR
Any help would be appreciated.
Thanks
Chris
If the stream on the link does not exist, creating VideoCapture object on that link would still be successful but you will not be able to process on the object.
You code's control flow just might be going in and checking if (cap.isOpened()== True) but there is no else block to handle what would happen if if (cap.isOpened() != True). So just try adding an else block to display the error message.
for x in feedList:
print("[INFO] Checking Link..." + x)
cap=cv2.VideoCapture(x)
try:
# Check if camera opened successfully
if (cap.isOpened()== True):
streamlink = x
print("[INFO] FOUND! Stream Link..." + x)
break
# Else is important to display error message on the screen if can.isOpened returns false
else
print("[NO STREAM]" + x)
except socket.error:
print("[NO STREAM]" + x)
except:
print("[FAILED]" + x)
pass
If this doesn't work: following might solve the issue:
One of the main issues is that every camera manufacturer uses their
own protocol (RTSP URI formatting). Finding the correct URL for your
IP-camera can be frustrating and time-intensive. When found you can
try to open it with VLC, and afterwards with Kerberos.io.
Depending on the format of the RTSP URI things can go wrong, for
example when using a format like above. To solve the problem you'll
need to add an question mark "?" at the end of the url.
As example original link might be:
rtsp://192.168.2.109:554/user=admin&password=mammaloe&channel=1&stream=0.sdp
So with ? it would be:
rtsp://192.168.2.109:554/user=admin&password=mammaloe&channel=1&stream=0.sdp?
Source

pubnub python 4 sdk

I have just started using pubnub. I entered the basic code which was given in pubnub python sdk (4.0) and I get the following errors
ERROR:pubnub:Async request Exception. 'Publish' object has no
attribute 'async' ERROR:pubnub:Exception in subscribe loop: 'Publish'
object has no attribute 'async' WARNING:pubnub:reconnection policy is
disabled, please handle reconnection manually.
As far as the async() is concerned, there is a troubleshoot in which the async error can be solved be entering the following
def callback(result, status):
if status.is_error():
print("Error %s" % str(status.error_data.exception))
print("Error category #%d" % status.category)
else:
print(str(result))\
but still it doesn't work.
This is the code
from pubnub.callbacks import SubscribeCallback
from pubnub.enums import PNStatusCategory
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
pnconfig = PNConfiguration()
pnconfig.subscribe_key = 'demo'
pnconfig.publish_key = 'demo'
pubnub = PubNub(pnconfig)
def my_publish_callback(envelope, status):
# Check whether request successfully completed or not
if not status.is_error():
pass # Message successfully published to specified channel.
else:
pass # Handle message publish error. Check 'category' property to find out possible issue
# because of which request did fail.
# Request can be resent using: [status retry];
class MySubscribeCallback(SubscribeCallback):
def presence(self, pubnub, presence):
pass # handle incoming presence data
def status(self, pubnub, status):
if status.category == PNStatusCategory.PNUnexpectedDisconnectCategory:
pass # This event happens when radio / connectivity is lost
elif status.category == PNStatusCategory.PNConnectedCategory:
# Connect event. You can do stuff like publish, and know you'll get it.
# Or just use the connected event to confirm you are subscribed for
# UI / internal notifications, etc
pubnub.publish().channel("awesomeChannel").message("hello!!").async(my_publish_callback)
elif status.category == PNStatusCategory.PNReconnectedCategory:
pass
# Happens as part of our regular operation. This event happens when
# radio / connectivity is lost, then regained.
elif status.category == PNStatusCategory.PNDecryptionErrorCategory:
pass
# Handle message decryption error. Probably client configured to
# encrypt messages and on live data feed it received plain text.
def message(self, pubnub, message):
pass # Handle new message stored in message.message
pubnub.add_listener(MySubscribeCallback())
pubnub.subscribe().channels('awesomeChannel').execute()
As the error is from the publish method, it could most probably be because
async has been changed to pn_async
Note that as on date, this is applicable only for Python3 as the same has not been implemented for Python 2.
Change
pubnub.publish().channel("awesomeChannel").message("hello!!").async(my_publish_callback)
to
pubnub.publish().channel("awesomeChannel").message("hello!!").pn_async(my_publish_callback)
Reference document here

Slack backend: is it possible to detect an edit?

Our errbot is providing links to JIRA tickets when it sees the right ticket patterns. Unfortunately, in slack it is common for users to edit their posts, and if both edits contain the JIRA ticket pattern, errbot provides the link twice, it is annoying.
Can I detect when a message is an edit as opposed to the original message?
Now you can because from the .extra argument on the messages you can get the message ID from slack.
This is not currently possible (on any of the backends), no. For chat networks which allow editing of messages, errbot currently injects edited messages as a new message.
If you need this functionality, please open an issue on errbot's issue tracker so we can brainstorm about how to possibly introduce this functionality.
My bot watches for and expands references to Jira issues too; one of the things I did was keep track of how many messages had been seen in the channel, and when the last time was that I responded to any given issue. Issues expanded within the last N (I use 10) messages are ignored. That way, if they edit the issue key itself they usually get a new expansion, but not if they edit other parts of the message.
def activate(self):
self.messages_seen={} # room: messages seen
self.last_shown={} # issue_key : { room : message number last shown }
super().activate()
def callback_message(self, msg):
if msg.is_group:
try:
self.messages_seen[msg.to.id] += 1
except KeyError:
self.messages_seen[msg.to.id] = 1
def record_expanded(self, msg, key, orig_key=None):
if msg.is_group:
channel=msg.to.id
keys = [ key, orig_key ] if orig_key and orig_key != key else [ key ]
for k in keys:
if k in self.last_shown:
self.last_shown[ k ][ channel ] = self.messages_seen[ channel ]
else:
self.last_shown[ k ] = { channel : self.messages_seen[ channel ] }
def should_expand(self, msg, key):
expand=False
if msg.body.split()[0] == 'jira':
# User said 'jira <key>', so always expand
expand=True
if msg.is_group:
channel=msg.to.id
message_number=self.messages_seen.get(channel, 1)
expanded_in = self.last_shown.get(key, {})
if expanded_in:
if channel not in expanded_in: # key has been expanded, but not in this channel
expand=True
else:
expanded_last_here = message_number - expanded_in[channel]
if expanded_last_here >= self.bot_config.JIRA_EXPAND_ONLY_EVERY: # not recently enough
expand=True
else:
self.log.debug("not expanding '%s' because expanded %d messages ago" % (key, expanded_last_here))
else:
# this key has not been seen anywhere before
expand=True
else:
# direct IM - always expand
expand=True
if not expand:
self.log.debug("Recently expanded %s, suppressing" % key)
return expand

Python - queuing one function

I've just started learning python, but I have problem with my code:
import pifacecad
# listener initialization
cad = pifacecad.PiFaceCAD()
listener = pifacecad.SwitchEventListener(chip=cad)
listener.register(4, pifacecad.IODIR_ON, blowMyMind)
listener.activate()
def blowMyMind(event):
print('some prints...')
time.sleep(4)
print('and the end.')
blowMyMind() will be fired as many times as listener it tells to. That is okay.
My goal is to deactivate listener UNTIL blowMyMind ends. Pifacecad suggest Barrier() to achieve that, at least I think that it was here for that reason(correct me if I'm wrong).
Now it's working as many times as I activate listener event, but It's not like pushing function 99 times at once, but queues it and runs one by one.
With Barriers I think it should look like this:
# Barrier
global end_barrier
end_barrier = Barrier(1)
# listener initialization
listener = pifacecad.SwitchEventListener(chip=cad)
listener.register(4, pifacecad.IODIR_ON, blowMyMind)
listener.activate()
def blowMyMind(event):
global end_barrier
test = end_barrier.wait()
print(test) # returns 0, which should not in about 5 seconds
print('some prints...')
time.sleep(4)
print('and the end.')
The funny part is when I change parties in Barrier initialization it is causing BrokenBarrierError at first listener event.
Actually I think that I completely misunderstood Barrier() I think the problem with it is that all listener events are in one thread instead of their own threads.
It's making me even more confused when I'm reading:
parties The number of threads required to pass the barrier.
from here: https://docs.python.org/3/library/threading.html
My conclusion: when initializing Barrier(X) it would be realeased when there will be X('or less', 'or more'?) number of threads. That sounds VERY stupid :D
I tried to make it that way with no luck:
# listener initialization
global busy
busy = 0
cad = pifacecad.PiFaceCAD()
listener = pifacecad.SwitchEventListener(chip=cad)
listener.register(4, pifacecad.IODIR_ON, blowMyMind)
listener.activate()
def blowMyMind(event):
global busy
if busy == 0:
busy = 1
print('some prints...')
time.sleep(4)
print('and the end.')
busy = 0
else:
return None

Resources