I am running a python file from a QProcess in a PyQt5 GUI.
I am showing everything from the terminal using readAllStandardOutput(I tried readAll also) to a textBox
In the python file, I am using telethon (library for using telegram API) python file is having a async event-handler in which I am printing something.
But before starting the starting the client readAllStandardOutput is reading fine. BUt after that print("Sent Hi!") is neither showing on the terminal nor Qprocess is reading it.
Pyqt5 GUI file having QProcess declaration.
class MainWindow(QMainWindow):
def __init__(self,parent = None):
super(QMainWindow, self).__init__(parent)
self.process = QProcess(self)
self.process.readyReadStandardOutput.connect(self.stdoutReady)
self.process.readyReadStandardError.connect(self.stderrReady)
self.process.started.connect(lambda: print('Started!'))
self.process.finished.connect(lambda: print('Finished!'))
self.process.setProcessChannelMode(QProcess.MergedChannels)
def startMain(self):
self.process.start('python3', ['mainApp2.py'])
def stdoutReady(self):
text = str(self.process.readAllStandardOutput())
self.mainTextBox.setText(str(text.strip()))
The python file QProess will start
import asyncio
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
print("Main App started")
api_id = #api_id integer
api_hash = "api_hash string"
client = TelegramClient('session', api_id, api_hash , loop = loop )
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
#client.on(events.NewMessage)
async def my_event_handler(event):
if 'hello' in event.raw_text:
await event.reply('Hi!')
await say_after(1, 'Sent Hi!')
client.start()
client.run_until_disconnected()
"Main app Started" is displayed on the text box but not "Sent Hi!".
And the event.reply('Hi!') is working fine.
I am not sure may be it is because it is creating a child process
I want to redirect all output from the terminal to the textBox in the GUI (if you have other methods do suggest).
Related
I'm trying to record twitch chat messages while still recognizing commands, however, recording the messages using event_message seems to stop the command from working.
Anybody know how you would recognize/respond to commands and record message at the same time?
from twitchio.ext import commands
import time
from datetime import datetime
class Bot(commands.Bot):
def __init__(self):
super().__init__(
token='oauth:',
prefix='!',
initial_channels=['channelname']
)
# This function isn't running; event_message is stealing the input!
#commands.command(name='ping')
async def my_command(self, ctx):
print('Caught ping')
await ctx.send('pong')
# records the messages (or in this case, the time a message is sent)
async def event_message(self, message):
print(time.mktime(datetime.now().timetuple())) # prints the unix second
if __name__ == '__main__':
bot = Bot()
bot.run()
Reducing this down even more, the same issue occurs here; with the bot responding to the command, but not printing the message author or content:
from twitchio.ext import commands
import os
bot = commands.Bot(
token='', prefix="!", initial_channels=[""], client_id='', nick='', client_secret='')
#This isn't working; Should still grab the message author/name and print.
Ive tried twitchio versions between 1.3.0 and 2.3.0, but the issue persists.
#bot.event
async def event_message(ctx):
print(ctx.author.name)
print(ctx.content)
await bot.handle_commands(ctx)
#This is running
#bot.command(name='test')
async def test_command(ctx):
await ctx.send('this is a test response')
if __name__ == '__main__':
bot.run()
Issue was not including an "await bot.handle_commands(ctx)" command below the event_message function.
Attached below is a working code
from twitchio.ext import commands
class Bot(commands.Bot):
def __init__(self):
super().__init__(token='', prefix="!", initial_channels=[""])
#commands.command()
async def ping(self, ctx):
print('Caught Ping')
await ctx.send(f'Pong!')
async def event_message(self, message):
print(message.author.name, message.content)
await bot.handle_commands(message) # <--This is the new line
bot = Bot()
bot.run()
NOTE: An error is raised by the event_message function when the bot sees one of its' own messages in chat. This doesn't seem to inhibit function, but it can be fixed by changing the code to the following:
async def event_message(self, message):
if hasattr(message.author, 'name'): # <-- New Line
print(message.author.name, message.content)
await bot.handle_commands(message)
I want to combine a TelegramBot with a PYQT application. I'm using telethon to listen to a channel and print out messages. However, this should run in the background, that is, the user should be able to interact with the GUI while the bot is listening to messages. So far, I've only managed to implement a blocking variant. That is, the gui is not showing because with client seems to be blocking.
class Win(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = uic.loadUi("layout.ui", self)
client = TelegramClient('session', TELE_ID, TELE_HASH)
#client.on(events.NewMessage(chats=["test"]))
async def m_listener(event):
print(event)
with client:
client.run_until_disconnected()
I also tried to move the last statement to a thread with
...
def run():
with client:
client.run_until_disconnected()
threading.Thread(target=run).start()
However, then I get a There is no current event loop in thread 'Thread-1'. error because with client tries to load the loop from asyncio which can only be done in the main thread.
Do you can give me any starting point on how to solve this?
It needs to create new async loop inside thread - so it needs to create new Client also in thread.
import asyncion
def run_bot():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
#client = TelegramClient('session', TELE_ID, TELE_HASH, loop=loop)
client = TelegramClient('session', TELE_ID, TELE_HASH) # also works
# ... code ...
with client:
client.run_until_disconnected()
# ---
threading.Thread(target=run_bot).start()
Working example which also use Queue (queue_in) to send command to bot.
It has also queue_out to send message from bot to PyQt but I didn't create code which could use this message.
PyQt has own class QThread which could be more useful because it uses singals to send messages.
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton
from PyQt5.QtCore import Qt
#from PyQt5.QtCore import QThread
from telethon.sync import TelegramClient, events
import asyncio
from queue import Queue
from threading import Thread
import os
TOKEN = os.getenv('TELEGRAM_TOKEN')
TELE_ID, TELE_HASH = TOKEN.split(":")
# --- classes ---
class MyWindow(QWidget):
def __init__(self, queue_in, queue_out):
super().__init__()
self.queue_in = queue_in
self.queue_out = queue_out
self.label = QLabel(self, text='Hello World', alignment=Qt.AlignCenter)
self.button = QPushButton(self, text='Stop Bot')
self.button.clicked.connect(self.stop_bot)
vbox = QVBoxLayout()
vbox.addWidget(self.label)
vbox.addWidget(self.button)
self.setLayout(vbox)
def stop_bot(self):
self.queue_in.put('stop')
# --- functions ---
def run_bot(queue_in, queue_out):
print("run_bot()")
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
print('loop:', loop)
client = TelegramClient('session', TELE_ID, TELE_HASH)#, loop=loop)
#client.on(events.NewMessage(pattern='/test'))
async def test_cmd(event):
#print('[BOT] event:', event)
print('[BOT] message:', event.message.message)
queue_out.put(event.message.message)
#client.on(events.NewMessage(pattern='/stop'))
async def stop_cmd(event):
print('[BOT] message:', event.message.message)
queue_out.put(event.message.message)
await client.disconnect() # it stops client
async def check_queue():
print('[BOT] check_queue(): start')
while True:
await asyncio.sleep(1)
#print('[BOT] check_queue(): check')
if not queue_in.empty():
cmd = queue_in.get()
print('[BOT] check_queue(): queue_in get:', cmd)
if cmd == 'stop':
await client.disconnect()
break
loop.create_task(check_queue())
with client:
print('[BOT] start')
client.run_until_disconnected()
print('[BOT] stop')
# --- main ---
queue_in = Queue() # to send data to bot
queue_out = Queue() # to receive data from bot
thread = Thread(target=run_bot, args=(queue_in, queue_out))
thread.start()
# - GUI -
print('PyQt')
app = QApplication([])
win = MyWindow(queue_in, queue_out)
win.show()
app.exec()
queue_in.put('stop')
thread.join()
I can't launch the browser in a separate thread.
I have this code:
import asyncio
from pyppeteer import launch
class browser(QThread):
def __init__(self):
QThread.__init__(self)
def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(self.main())
async def main(self):
url = 'https://example.com'
browser = await launch(headless=True)
page = await browser.newPage()
await page.goto(url, waitUntil='networkidle0')
When calling, this error comes out:
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread of the main interpreter
If you run it without QThread then everything works
I am using the websocket library in Python and I am new to this.
I want to create multiple different connections to websockets. This happens through my custom WebsocketProcess class which opens the connection, receives the event, keeps a record Id and then calls an API to grab the information for this particular record.
I am having trouble running them in parallel.
Please, see below (ignore the numerous imports)
Main:
#if __name__ == "__main__":
async def main():
#The length AccessTokens, ClientDescriptions and SQLTablesFix determines how many websockets we need to open
L = await asyncio.gather(
properties[0].websocket_starting(AccessTokens[0], ClientDescriptions[0], SQLTablesFix[0]),
properties[1].websocket_starting(AccessTokens[1], ClientDescriptions[1], SQLTablesFix[1]),
...
...
)
asyncio.run(main())
The WebsocketProcess class is as follows:
class WebsocketProcess:
"""description of class"""
def on_error(self, ws, error):
#{Relevant Code Here}
def on_open(self, ws):
print("\nOn Open\n")
def run(*args):
while True:
try:
time.sleep(1)
except TimeoutError:
pass
ws.close()
def on_close(self):
#{Relevant Code Here}
def on_message(self, ws, message):
#{Relevant Code Here}
ws.close()
def connect_websocket(self, AccessToken, ClientDescription, SQLTablesFix):
ws = websocket.WebSocketApp("_______url_here_____",
on_open = self.on_open,
on_message = self.on_message,
on_error = self.on_error,
on_close = self.on_close,
cookie = "ClientToken=_______; AccessToken=%s" % AccessToken)
ws.run_forever()
async def websocket_starting(self, AccessToken, ClientDescription, SQLTablesFix):
print("\nwebsocket_starting")
self.AccessToken = AccessToken
self.ClientDescription = ClientDescription
self.SQLTablesFix = SQLTablesFix
self.connect_websocket(self.AccessToken, self.ClientDescription, self.SQLTablesFix)
As you can see from the above, I have changed the main to asynchronous to run multiple instances of the websocket_process class in parallel. It opens a connection to the first websocket and it stops there waiting for events, without proceeding to open a second websocket.
I tried making the WebsocketProcess class entirely asynchronous but the errors I am receiving an error specifying that coroutine 'run' was never awaited (in the connect_websocket method).
Do you guys have any suggestions on how to run multiple instances of the websocket_process class in parallel?
Thanks!
Your websocket operations are blocking operations, to use websocket in asyncio, use other async libraries like websockets, Tornado
I am currently developing a server program in Python that uses the websockets and asyncio packages.
I got a basic script handling websockets working (Exhibit A). This script locks when waiting for input, which is not what I want.
The solution for this that I imagine is I can start two asynchronous tasks - one that handles inputs and one that handles outputs - and start them in a secondary event loop. I had to do some research about coroutines, and I came up with Exhibit B as a proof of concept for running two things simultaneously in an event loop.
Now what I'm stuck on is Exhibit C. When I attempted to use this in a practical scenario with the websockets package, I found that websocket.recv() never finishes (or the coroutine never un-pauses - I'm not sure what's going on exactly). In exhibit A it works fine, and I've determined that the coroutine definitely runs at least up until that point.
Any ideas?
Exhibit A:
#!/usr/bin/python3
import asyncio
import websockets
import time
# This works great!
async def hello(websocket, path):
while True:
# This line waits for input from socket
name = await websocket.recv()
print("< {}".format(name))
# "echo... echo... echo... echo... echo..."
greeting = ''.join(name + "... " for x in range(5))
await websocket.send(greeting)
print("> {}".format(greeting))
time.sleep(0.1);
start_server = websockets.serve(hello, '', 26231)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Exhibit B:
#!/usr/bin/python3
import asyncio
import time
class Thing:
def __init__(self):
pass
async def test(self):
for x in range(10):
print("Hello"+str(x))
await asyncio.sleep(0)
def run(self):
# Add the task to the event loop twice
asyncio.ensure_future(self.test())
asyncio.ensure_future(self.test())
t = Thing()
t.run()
loop = asyncio.get_event_loop();
loop.run_forever()
Exhibit C:
#!/usr/bin/python3
import asyncio
import websockets
import time
class WebsocketRequest:
def __init__(self, websocket):
self.websocket = websocket
# Works great
async def handle_oputs(self):
# This works fine - sends a message
# every 10 seconds to the client
while True:
print("sending...")
await self.websocket.send("Hello")
print("> {}".format("Hello"))
time.sleep(10)
# Doesn't work
async def handle_iputs(self):
# This stops at the await and never receives
# any input from the client :/
while True:
try:
print("receiving...")
# This is the line that causes sadness
data = await self.websocket.recv()
print("< {}".format(data))
except:
# This doesn't happen either
print("Listener is dead")
async def run(self):
# This is the part where the coroutine for
# a client get split off into two of them
# to handle input and output separately.
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
asyncio.ensure_future(self.handle_iputs())
asyncio.ensure_future(self.handle_oputs())
loop.run_forever()
class WebsocketServer:
def __init__(self, address):
self.ip = address[0]
self.port = address[1]
async def hello(self, websocket, path):
req = WebsocketRequest(websocket)
await req.run()
def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
start_server = websockets.serve(self.hello, self.ip, self.port)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Maybe the module websocket (unlike websockets) can help you.
The use of WebsocketApp is very simple:
from websocket import WebSocketApp
class ExampleClass(object):
def __init__(self):
websocket.enableTrace(True)
self.ws = websocket.WebSocketApp("ws://echo.websocket.org",
on_message=on_message,
on_error=on_error,
on_close=on_close)
def on_message(ws, msg):
print "Message Arrived:" + msg
def on_error(ws, error):
print error
def on_close(ws):
print "Connection Closed"
def on_open(ws):
ws.send("Hello!")
To download this module: https://pypi.python.org/pypi/websocket-client
time.sleep() is a blocking operation, so any other tasks cannot interrupt and are not scheduled. Use await asyncio.sleep() instead.