Как подписаться на событие .NET в прослушивателе python с помощью pythonnet?
Я пытаюсь настроить прослушиватель событий для подписки на событие тика (цены) из торгового приложения FX, используя Python. Исходное приложение - это собственное 32-разрядное приложение для Windows под названием MetaTrader4 . У него нет API, поэтому мост mtapi был разработан в .NET, чтобы другие языки программирования могли взаимодействовать с ним. В приложении определены некоторые события, два из которых: QuoteUpdateи QuoteUpdated.
Итак, я хотел бы написать слушателя ( делегата ?), Используя python.net, чтобы подписаться на это событие. Но поскольку я не могу понять, как код .NET создает эти события, и как правильно использовать pythonnet , мне не удалось заставить это работать. Я тоже постоянно сталкиваюсь с ошибкой:
TypeError: 'EventBinding' object is not callable
Поиск в Google не вернул ничего полезного, кроме этого комментария "FIXME".
Вот мой код:
import os, sys, clr
sys.path.append(r"C:\Program Files\MtApi")
asm = clr.AddReference('MtApi')
import MtApi as mt
res = 0
def printTick(symbol, ask, bid):
print('Tick: Symbol: {} Ask: {:.5f} Bid: {:.5f}'.format(symbol, ask, bid))
# Setup .NET API bridge connection
mtc = mt.MtApiClient()
res = mtc.BeginConnect('127.0.0.1', 8222);
#--------------------------------------
# Register and use the listener
#--------------------------------------
# This does NOT work!
mtc.QuoteUpdate += printTick
#...
Намерение моего кода должно быть ясным.
Q: Как я могу заставить мой слушатель активироваться при получении QuoteUpdateсобытия .NET?
Для справки:
- Код .NET на C # (из MtApiClient.cs выглядит так:
...
private void _client_QuoteUpdated(MTApiService.MtQuote quote) {
if (quote != null) {
QuoteUpdate?.Invoke(this, new MtQuoteEventArgs(new MtQuote(quote)));
QuoteUpdated?.Invoke(this, quote.Instrument, quote.Bid, quote.Ask);
}
}
...
public event MtApiQuoteHandler QuoteUpdated;
public event EventHandler<MtQuoteEventArgs> QuoteUpdate;
public event EventHandler<MtQuoteEventArgs> QuoteAdded;
public event EventHandler<MtQuoteEventArgs> QuoteRemoved;
- А небольшое приложение VB Test выглядит так:
Imports MtApi
Public Class Form1
Private apiClient As MtApiClient
Public Sub New()
InitializeComponent()
apiClient = New MtApiClient
AddHandler apiClient.QuoteUpdated, AddressOf QuoteUpdatedHandler
End Sub
Sub QuoteUpdatedHandler(sender As Object, symbol As String, bid As Double, ask As Double)
Dim quoteSrt As String
quoteSrt = symbol + ": Bid = " + bid.ToString() + "; Ask = " + ask.ToString()
ListBoxQuotesUpdate.Invoke(Sub()
ListBoxQuotesUpdate.Items.Add(quoteSrt)
End Sub)
Console.WriteLine(quoteSrt)
End Sub
Private Sub btnConnect_Click(sender As System.Object, e As System.EventArgs) Handles btnConnect.Click
apiClient.BeginConnect(8222)
End Sub
Private Sub btnDisconnect_Click(sender As System.Object, e As System.EventArgs) Handles btnDisconnect.Click
apiClient.BeginDisconnect()
End Sub
End Class
ОБНОВИТЬ
Для справки, у нас есть следующие соответствующие вызовы DLL, заданные атрибутами, типами и __doc__:
attr: QuoteAdded type: <class 'CLR.EventBinding'> doc: <n/a>
attr: QuoteRemoved type: <class 'CLR.EventBinding'> doc: <n/a>
attr: QuoteUpdate type: <class 'CLR.EventBinding'> doc: <n/a>
attr: QuoteUpdated type: <class 'CLR.EventBinding'> doc: <n/a>
attr: add_QuoteAdded type: <class 'CLR.MethodBinding'> doc: Void add_QuoteAdded(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: add_QuoteRemoved type: <class 'CLR.MethodBinding'> doc: Void add_QuoteRemoved(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: add_QuoteUpdate type: <class 'CLR.MethodBinding'> doc: Void add_QuoteUpdate(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: add_QuoteUpdated type: <class 'CLR.MethodBinding'> doc: Void add_QuoteUpdated(MtApi.MtApiQuoteHandler)
attr: remove_QuoteAdded type: <class 'CLR.MethodBinding'> doc: Void remove_QuoteAdded(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: remove_QuoteRemoved type: <class 'CLR.MethodBinding'> doc: Void remove_QuoteRemoved(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: remove_QuoteUpdate type: <class 'CLR.MethodBinding'> doc: Void remove_QuoteUpdate(System.EventHandler`1[MtApi.MtQuoteEventArgs])
attr: remove_QuoteUpdated type: <class 'CLR.MethodBinding'> doc: Void remove_QuoteUpdated(MtApi.MtApiQuoteHandler)
Похожие вопросы:
Существует буквально сотня связанных проблем SO, и я, вероятно, рассмотрел более 60% из них, но практически безуспешно с точки зрения применимости к конкретному варианту использования. Некоторые из них:
- Поддерживают ли классы Python события, как и другие языки?
- Как правильно преобразовать регистрацию обработчика событий с C # на VB.net?
- Как преобразовать делегата VB в обработчик событий Python?
- https://ironpython.net/documentation/dotnet/dotnet.html (также может быть актуально)
- https://openbookproject.net/thinkcs/python/english3e/events.html
- https://code.activestate.com/recipes/410686-c-style-events-in-python/
Ответы
Через несколько дней и несколько долгих часов я обнаружил комбинацию ошибок. Как всегда, сочетание 2-3 простых ошибок может надолго сделать вашу жизнь несчастной. Но самая большая ошибка все, была дурачит думать , что у слушателя (иначе. Делегат ) должен был быть сложным с кучей __init__и __iadd__функциями. Неправильно! Python.NET просто работает большую часть времени, но ошибки, которые вы получаете (если они вообще есть), бесполезны, если вы сделаете небольшую ошибку.
В моем исходном коде есть 1.5 проблемы.
Было 2 разных функции Quote , а именно:
QuoteUpdateкоторый возвращает отправителя и объект с именем «MtQuoteEventArgs» в .NET.QuoteUpdatedкоторый возвращает отправителя и 3 аргумента .Поэтому нам нужно исправить как функцию printTick (), так и строку прослушивателя.
Исправленный код:
def printTick(source, symbol, ask, bid):
print('Tick: Symbol: {} Ask: {:.5f} Bid: {:.5f}'.format(symbol, ask, bid))
...
mtc.QuoteUpdates += printTick
Для получения дополнительной информации о событиях и делегатах C # :
- https://www.tutorialsteacher.com/csharp/csharp-event
Согласно документации mtapi, которую вы связали, код должен быть:
def printTick(sender, args):
print(str(args.Quote.Instrument))
mtc = mt.MtApiClient()
res = mtc.BeginConnect('127.0.0.1', 8222)
mtc.QuoteUpdate += printTick # Subscribe & handle new repeated events
rA = mtc.SymbolInfoTick(SYM) # Make a request to get a one-time tick data
# Need wait loop here