My PyQt5 application is a GUI that runs a python script, another_module.py, within a QProcess. another_module.py has a logging mechanism that writes to stdout. I would like to be able to connect the logging in another_module.py and redirect it to a text box in the GUI such that whatever logging another_module.py generates be printed/appended to the text box in my GUI. My code is in below.
my_gui.py
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import QProcess
import logging
import another_module # This file has a sample logging mechanism which I would like to have access in my_gui.py
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(name)s - %(levelname)s - %(message)s')
class QTextEditLogger(logging.Handler):
def __init__(self, parent):
super().__init__()
self.widget = QtWidgets.QPlainTextEdit(parent)
self.widget.setReadOnly(True)
def emit(self, record):
msg = self.format(record)
self.widget.appendPlainText(msg)
class MyDialog(QtWidgets.QDialog, QtWidgets.QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent)
# Setup logging here:
logTextBox = QTextEditLogger(self)
logTextBox.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logging.getLogger().addHandler(logTextBox) # root logging nicely redirects to logTextBox
logging.getLogger().setLevel(logging.DEBUG)
another_module.logger.addHandler(logTextBox) # logging of the other file is not redirected to logTextBox
another_module.logger.setLevel(logging.DEBUG)
self.process = None
self._button = QtWidgets.QPushButton(self)
self._button.setText('Start')
layout = QtWidgets.QVBoxLayout()
layout.addWidget(logTextBox.widget)
layout.addWidget(self._button)
self.setLayout(layout)
self._button.clicked.connect(self.test)
def test(self):
logging.debug('damn, a bug')
logging.info('something to remember')
logging.warning('that\'s not right')
logging.error('foobar')
self.process = QProcess(self)
self.process.started.connect(lambda: print('Started!'))
self.process.finished.connect(lambda: print('Finished!'))
self.process.startDetached('python another_module.py')
app = QtWidgets.QApplication(sys.argv)
dlg = MyDialog()
dlg.show()
dlg.raise_()
sys.exit(app.exec_())
another_module.py
import logging
import sys
import time
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s:%(name)s:%(funcName)s:%(lineno)d:%(message)s')
file_handler = logging.FileHandler('another_module.log')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
def create_some_log():
while True:
logger.debug('<<different file>> damn, a bug')
logger.info('<<different file>> something to remember')
logger.warning('<<different file>> that\'s not right')
logger.error('<<different file>> foobar')
time.sleep(5)
if __name__ == '__main__':
create_some_log()
How can I redirect the output of the logging inside another_module.py to the textbox in my_gui.py?
My code above runs with no problem and the console prints the following information:
2020-10-04 22:39:23,975 - root - DEBUG - damn, a bug
2020-10-04 22:39:23,976 - root - INFO - something to remember
2020-10-04 22:39:23,976 - root - WARNING - that's not right
2020-10-04 22:39:23,976 - root - ERROR - foobar
2020-10-04 22:49:27,457:__main__:create_some_log:22:<<different file>> something to remember
2020-10-04 22:49:27,457:__main__:create_some_log:23:<<different file>> that's not right
2020-10-04 22:49:27,457:__main__:create_some_log:24:<<different file>> foobar
However, only the root logging is printed to the text box in the gui:
2020-10-04 22:39:23,975 - root - DEBUG - damn, a bug
2020-10-04 22:39:23,976 - root - INFO - something to remember
2020-10-04 22:39:23,976 - root - WARNING - that's not right
2020-10-04 22:39:23,976 - root - ERROR - foobar
If you are going to run the script with QProcess then it will run in a different process so doing another_module.logger.addHandler(logTextBox) and another_module.logger.setLevel(logging.DEBUG) will not modify anything or make sense. The idea in this case is to get the log using the readAllStandardOutput() method:
import logging
import os
import sys
from PyQt5 import QtCore, QtWidgets
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
logging.basicConfig(
level=logging.DEBUG, format=" %(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
class QTextEditLogger(logging.Handler):
def __init__(self, parent):
super().__init__()
self.widget = QtWidgets.QPlainTextEdit(parent)
self.widget.setReadOnly(True)
def emit(self, record):
msg = self.format(record)
self.widget.appendPlainText(msg)
class MyDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
# Setup logging here:
self.logTextBox = QTextEditLogger(self)
self.logTextBox.setFormatter(
logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
)
logging.getLogger().addHandler(self.logTextBox)
logging.getLogger().setLevel(logging.DEBUG)
self._button = QtWidgets.QPushButton()
self._button.setText("Start")
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.logTextBox.widget)
layout.addWidget(self._button)
self._button.clicked.connect(self.test)
self.process = QtCore.QProcess()
self.process.readyReadStandardOutput.connect(
self.handle_readyReadStandardOutput
)
self.process.started.connect(lambda: print("Started!"))
self.process.finished.connect(lambda: print("Finished!"))
def test(self):
logging.debug("damn, a bug")
logging.info("something to remember")
logging.warning("that's not right")
logging.error("foobar")
script = os.path.join(CURRENT_DIR, "another_module.py")
self.process.start(sys.executable, [script])
def handle_readyReadStandardOutput(self):
text = self.process.readAllStandardOutput().data().decode()
self.logTextBox.widget.appendPlainText(text.strip())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
dlg = MyDialog()
dlg.show()
dlg.raise_()
sys.exit(app.exec_())
I have a tkinter app running alongside two different threads that are logging into it using a queue.
One of the threads is in the same code file as the tkinter app. The other one is imported from another file, even thought their code is similar. What I verify is that only the thread defined in the same file manages to write into the UI. Do you know why this happens?
The code for the main file is:
import time
import queue
import threading
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
from tkinter import ttk
import logging
from logging.handlers import QueueHandler
from foo import ImportedLoggerThread
logger = logging.getLogger(__name__)
class LoggerThread(threading.Thread):
def __init__(self):
super().__init__()
self._stop_event = threading.Event()
def run(self):
logger.debug('LoggerThread: running')
i = 0
while not self._stop_event.is_set():
logger.info("LoggerThread: iteration %d" % i)
i += 1
time.sleep(1)
def stop(self):
self._stop_event.set()
class LoggingWindow:
def __init__(self, frame):
self.frame = frame
self.scrolled_text = ScrolledText(frame, height=12)
self.scrolled_text.pack()
self.log_queue = queue.Queue()
self.queue_handler = QueueHandler(self.log_queue)
logger.addHandler(self.queue_handler)
# start polling
self.frame.after(100, self.poll_log_queue)
def write(self, record):
msg = self.queue_handler.format(record)
self.scrolled_text.insert(tk.END, msg + '\n')
# Scroll to the bottom
self.scrolled_text.yview(tk.END)
def poll_log_queue(self):
# Poll every 100ms
while True:
try:
record = self.log_queue.get(block=False)
except queue.Empty:
break
else:
self.write(record)
self.frame.after(100, self.poll_log_queue)
class App:
def __init__(self, root):
self.root = root
frame = ttk.Labelframe(text="Log")
frame.pack()
self.console = LoggingWindow(frame)
self.th = LoggerThread()
self.th.start()
self.imported = ImportedLoggerThread()
self.imported.start()
self.root.protocol('WM_DELETE_WINDOW', self.quit)
def quit(self):
self.th.stop()
self.imported.stop()
self.root.destroy()
def main():
logging.basicConfig(level=logging.DEBUG)
root = tk.Tk()
app = App(root)
app.root.mainloop()
if __name__ == '__main__':
main()
and for the second file foo.py:
import threading
import logging
import time
logger = logging.getLogger(__name__)
class ImportedLoggerThread(threading.Thread):
def __init__(self):
super().__init__()
self._stop_event = threading.Event()
def run(self):
logger.debug('Imported: running')
i = 0
while not self._stop_event.is_set():
logger.info("Imported: iteration %d" % i)
i += 1
time.sleep(2)
def stop(self):
self._stop_event.set()
Thanks in advance!
You define 2 logger instances in your files (logger = logging.getLogger(__name__)) and it causes your issue. If you use the same logger instance, it should work. It means in your case, you should pass the logger instance from your main file to the imported module (foo.py). Please see below the fixed foo.py and the fixed App class in the main file.
foo.py:
import threading
import time
class ImportedLoggerThread(threading.Thread):
def __init__(self, my_logger):
super().__init__()
self._stop_event = threading.Event()
self.my_logger = my_logger # Should be passed from caller side.
def run(self):
self.my_logger.debug('Imported: running')
i = 0
while not self._stop_event.is_set():
self.my_logger.info("Imported: iteration %d" % i)
i += 1
time.sleep(2)
def stop(self):
self._stop_event.set()
As you can see above the "imported" module uses a getting logger (It should comes from the "main" file)
App class:
class App:
def __init__(self, root):
self.root = root
frame = ttk.Labelframe(text="Log")
frame.pack()
self.console = LoggingWindow(frame)
self.th = LoggerThread()
self.th.start()
self.imported = ImportedLoggerThread(my_logger=logger) # Should be passed the defined logger instance.
self.imported.start()
self.root.protocol('WM_DELETE_WINDOW', self.quit)
def quit(self):
self.th.stop()
self.imported.stop()
self.root.destroy()
As you can see in the App class, the defined logger instance is passed to the imported ImportedLoggerThread class.
Output:
>>> python3 test.py
DEBUG:__main__:LoggerThread: running
DEBUG:__main__:Imported: running
INFO:__main__:LoggerThread: iteration 0
INFO:__main__:Imported: iteration 0
INFO:__main__:LoggerThread: iteration 1
INFO:__main__:Imported: iteration 1
INFO:__main__:LoggerThread: iteration 2
GUI:
I am trying to build a GUI for my Twitter application that automatically follows people. I have it mostly working in the console, however I am trying to convert it into a GUI, and am running into a few issues. The main problem is that whenever I assign the script I want to run when a button is pushed, it either runs immediately when I run the app, which wont display the GUI; or the GUI displays, but the function on the button does nothing. I have been looking for days, but I do not know what the problem is.
#! /usr/bin/python
import time
import tweepy
from keys import keys
import requests_cache
from random import randint
from PyQt4 import QtGui
from PyQt4 import QtCore
from subprocess import call
import sys
from AddToDB import *
SCREEN_NAME = 'JonahHensley'
CONSUMER_KEY = keys['consumer_key']
CONSUMER_SECRET = keys['consumer_secret']
ACCESS_TOKEN = keys['access_token']
ACCESS_TOKEN_SECRET = keys['access_token_secret']
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
api = tweepy.API(auth)
followers = api.followers_ids(SCREEN_NAME)
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
btn = QtGui.QPushButton('Yes', self)
btn.clicked.connect(follower.follow(self))
btn.resize(180, 40)
btn.move(20, 35)
qbtn = QtGui.QPushButton('No', self)
qbtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
qbtn.resize(180, 40)
qbtn.move(20, 80)
self.setWindowTitle('Test')
self.show()
class follower(QtGui.QWidget):
def follow(self):
while True:
try:
for f in followers:
UserFollow = input("Follow users from: ")
print('Getting followers from ' + UserFollow)
api.wait_on_rate_limit = True
api.wait_on_rate_limit_notify = True
requests_cache.install_cache(cache_name='twitter_cache', backend='sqlite', expire_after=180)
print("followed {0}".format(api.get_user(f).screen_name))
api.create_friendship(f)
randnum = randint(10, 25)
randnumdisplay = print("Delaying next follow for " + str(randnum) + " seconds.")
print(str(randnumdisplay))
screen_names = [api.get_user(f).screen_name]
time.sleep(randnum)
except tweepy.TweepError:
print("Waiting till next interval...")
time.sleep(60 * 15)
continue
except StopIteration:
def main():
app = QtGui.QApplication(sys.argv)
gui = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main() break
def main():
app = QtGui.QApplication(sys.argv)
gui = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Im trying to create GUI app for my test project, based on Python/Twisted/Autobahn.ws. Im following article as like there: wxPython and Twisted and try to connect my application to the server. But at the start i'll get an error:
Unhandled Error
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/twisted/python/log.py", line 88, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "/usr/local/lib/python2.7/dist-packages/twisted/python/log.py", line 73, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "/usr/local/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "/usr/local/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext
return func(*args,**kw)
--- <exception caught here> ---
File "/usr/local/lib/python2.7/dist-packages/twisted/internet/_threadedselect.py", line 283, in _doReadOrWrite
why = getattr(selectable, method)()
File "/usr/local/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 593, in doConnect
self._connectDone()
File "/usr/local/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 607, in _connectDone
self.protocol = self.connector.buildProtocol(self.getPeer())
File "/usr/local/lib/python2.7/dist-packages/twisted/internet/base.py", line 1071, in buildProtocol
return self.factory.buildProtocol(addr)
File "/usr/local/lib/python2.7/dist-packages/twisted/protocols/policies.py", line 171, in buildProtocol
return self.protocol(self, self.wrappedFactory.buildProtocol(addr))
File "/usr/local/lib/python2.7/dist-packages/twisted/internet/protocol.py", line 123, in buildProtocol
p = self.protocol()
exceptions.AttributeError: GUIClientProtocol instance has no __call__ method
How i can fix this issue? Need to define call method in my class or maybe i have better solution for this?
Code:
1) client_gui:
import wx
from twisted.internet import wxreactor
wxreactor.install()
from twisted.internet import reactor, ssl
from autobahn.twisted.websocket import WebSocketClientFactory, WebSocketClientProtocol, connectWS
from gui.filemanager import CloudStorage
class GUIClientProtocol(WebSocketClientProtocol):
def __init__(self, gui):
self.gui = gui
if __name__ == '__main__':
app = wx.App(False)
frame = CloudStorage(None, -1, 'CloudStorage')
frame.Show()
host_url = "wss://%s:%s" % ("localhost", 9000)
# create a WS server factory with our protocol
factory = WebSocketClientFactory(host_url, debug = False)
factory.protocol = GUIClientProtocol(frame)
# SSL client context: using default
if factory.isSecure:
contextFactory = ssl.ClientContextFactory()
else:
contextFactory = None
reactor.registerWxApp(app)
connectWS(factory, contextFactory)
reactor.run()
2) server.py
import sys
import datetime
import pickle
from json import dumps, loads
from hashlib import sha256
from time import gmtime, strftime
from subprocess import Popen, PIPE, STDOUT
import sqlalchemy
import sqlalchemy.exc
from sqlalchemy import and_, func, asc
from sqlalchemy.orm import sessionmaker
from twisted.internet import reactor, ssl
from twisted.internet.task import deferLater
from twisted.python import log, logfile
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol, listenWS
from balancer.balancer import Balancer
from db.tables import File as FileTable
from db.tables import Users, FileServer, FileSpace, Catalog
from flask_app import app
from utils import commands
log_file = logfile.LogFile("service.log", ".")
log.startLogging(log_file)
engine = sqlalchemy.create_engine('postgresql://user:password#localhost/csan', pool_size=20, max_overflow=0)
def checkServerStatus(ip, port):
p = Popen(["python", "statuschecker.py", str(ip), str(port)], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
result = p.communicate()[0].replace('\n', '')
return result.split('|')
class DFSServerProtocol(WebSocketServerProtocol):
... # ~450 lines of code
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'debug':
log.startLogging(sys.stdout)
debug = True
port = int(sys.argv[2])
else:
debug = False
port = int(sys.argv[1])
contextFactory = ssl.DefaultOpenSSLContextFactory('web/keys/server.key', 'web/keys/server.crt')
server_addr = "wss://localhost:%d" % (port)
factory = WebSocketServerFactory(server_addr, debug = debug, debugCodePaths = debug)
factory.protocol = DFSServerProtocol
factory.setProtocolOptions(allowHixie76 = True)
listenWS(factory, contextFactory)
# Flask with SSL under Twisted
resource = WSGIResource(reactor, reactor.getThreadPool(), app)
site = Site(resource)
reactor.listenSSL(8080, site, contextFactory)
# reactor.listenTCP(8080, web)
reactor.run()
Update #1:
client_gui.py looks like that:
import wx
from twisted.internet import wxreactor
wxreactor.install()
from twisted.internet import reactor, ssl
from autobahn.twisted.websocket import WebSocketClientFactory, WebSocketClientProtocol, connectWS
from gui.filemanager import CloudStorage
class GUIClientProtocol(WebSocketClientProtocol):
def __init__(self):
WebSocketClientProtocol.__init__(self)
self.gui = None
def connectionMade(self):
self.gui = self.factory._frame
self.gui.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = CloudStorage(None, -1, 'CloudStorage')
# create a WS server factory with our protocol
host_url = "wss://%s:%s" % ("localhost", 9000)
factory = WebSocketClientFactory(host_url)
factory.protocol = GUIClientProtocol
factory._frame = frame
# SSL client context: using default
if factory.isSecure:
contextFactory = ssl.ClientContextFactory()
else:
contextFactory = None
reactor.registerWxApp(app)
connectWS(factory, contextFactory)
reactor.run()
Update #2:
code of my CloudStorage class:
import wx
import os
import time
ID_BUTTON=100
ID_EXIT=200
ID_SPLITTER=300
class MyListCtrl(wx.ListCtrl):
def __init__(self, parent, id):
wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT)
files = os.listdir('.')
images = ['images/empty.png', 'images/folder.png', 'images/source_py.png',
'images/image.png', 'images/pdf.png', 'images/up16.png']
self.InsertColumn(0, 'Name')
self.InsertColumn(1, 'Ext')
self.InsertColumn(2, 'Size', wx.LIST_FORMAT_RIGHT)
self.InsertColumn(3, 'Modified')
self.SetColumnWidth(0, 220)
self.SetColumnWidth(1, 70)
self.SetColumnWidth(2, 100)
self.SetColumnWidth(3, 420)
self.il = wx.ImageList(16, 16)
for i in images:
self.il.Add(wx.Bitmap(i))
self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
j = 1
self.InsertStringItem(0, '..')
self.SetItemImage(0, 5)
for i in files:
(name, ext) = os.path.splitext(i)
ex = ext[1:]
size = os.path.getsize(i)
sec = os.path.getmtime(i)
self.InsertStringItem(j, name)
self.SetStringItem(j, 1, ex)
self.SetStringItem(j, 2, str(size) + ' B')
self.SetStringItem(j, 3, time.strftime('%Y-%m-%d %H:%M',
time.localtime(sec)))
# if os.path.isdir(i):
# self.SetItemImage(j, 1)
# elif ex == 'py':
# self.SetItemImage(j, 2)
# elif ex == 'jpg':
# self.SetItemImage(j, 3)
# elif ex == 'pdf':
# self.SetItemImage(j, 4)
# else:
# self.SetItemImage(j, 0)
if (j % 2) == 0:
self.SetItemBackgroundColour(j, '#e6f1f5')
j = j + 1
class CloudStorage(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, -1, title)
self.splitter = wx.SplitterWindow(self, ID_SPLITTER, style=wx.SP_BORDER)
self.splitter.SetMinimumPaneSize(50)
p1 = MyListCtrl(self.splitter, -1)
p2 = MyListCtrl(self.splitter, -1)
self.splitter.SplitVertically(p1, p2)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_SPLITTER_DCLICK, self.OnDoubleClick, id=ID_SPLITTER)
filemenu= wx.Menu()
filemenu.Append(ID_EXIT,"E&xit"," Terminate the program")
editmenu = wx.Menu()
netmenu = wx.Menu()
showmenu = wx.Menu()
configmenu = wx.Menu()
helpmenu = wx.Menu()
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File")
menuBar.Append(editmenu, "&Edit")
menuBar.Append(netmenu, "&Net")
menuBar.Append(showmenu, "&Show")
menuBar.Append(configmenu, "&Config")
menuBar.Append(helpmenu, "&Help")
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.OnExit, id=ID_EXIT)
tb = self.CreateToolBar( wx.TB_HORIZONTAL | wx.NO_BORDER |
wx.TB_FLAT | wx.TB_TEXT)
# tb.AddSimpleTool(10, wx.Bitmap('images/previous.png'), 'Previous')
# tb.AddSimpleTool(20, wx.Bitmap('images/up.png'), 'Up one directory')
# tb.AddSimpleTool(30, wx.Bitmap('images/home.png'), 'Home')
# tb.AddSimpleTool(40, wx.Bitmap('images/refresh.png'), 'Refresh')
# tb.AddSeparator()
# tb.AddSimpleTool(50, wx.Bitmap('images/write.png'), 'Editor')
# tb.AddSimpleTool(60, wx.Bitmap('images/terminal.png'), 'Terminal')
# tb.AddSeparator()
# tb.AddSimpleTool(70, wx.Bitmap('images/help.png'), 'Help')
tb.Realize()
self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
button1 = wx.Button(self, ID_BUTTON + 1, "F3 View")
button2 = wx.Button(self, ID_BUTTON + 2, "F4 Edit")
button3 = wx.Button(self, ID_BUTTON + 3, "F5 Copy")
button4 = wx.Button(self, ID_BUTTON + 4, "F6 Move")
button5 = wx.Button(self, ID_BUTTON + 5, "F7 Mkdir")
button6 = wx.Button(self, ID_BUTTON + 6, "F8 Delete")
button7 = wx.Button(self, ID_BUTTON + 7, "F9 Rename")
button8 = wx.Button(self, ID_EXIT, "F10 Quit")
self.sizer2.Add(button1, 1, wx.EXPAND)
self.sizer2.Add(button2, 1, wx.EXPAND)
self.sizer2.Add(button3, 1, wx.EXPAND)
self.sizer2.Add(button4, 1, wx.EXPAND)
self.sizer2.Add(button5, 1, wx.EXPAND)
self.sizer2.Add(button6, 1, wx.EXPAND)
self.sizer2.Add(button7, 1, wx.EXPAND)
self.sizer2.Add(button8, 1, wx.EXPAND)
self.Bind(wx.EVT_BUTTON, self.OnExit, id=ID_EXIT)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.splitter,1,wx.EXPAND)
self.sizer.Add(self.sizer2,0,wx.EXPAND)
self.SetSizer(self.sizer)
size = wx.DisplaySize()
self.SetSize(size)
self.sb = self.CreateStatusBar()
self.sb.SetStatusText(os.getcwd())
self.Center()
self.Show(True)
def OnExit(self,e):
self.Close(True)
def OnSize(self, event):
size = self.GetSize()
self.splitter.SetSashPosition(size.x / 2)
self.sb.SetStatusText(os.getcwd())
event.Skip()
def OnDoubleClick(self, event):
size = self.GetSize()
self.splitter.SetSashPosition(size.x / 2)
if __name__ == '__main__':
app = wx.App(0)
CloudStorage(None, -1, 'CloudStorage')
app.MainLoop()
Update: I have created a complete example of using wxPython with Autobahn to create WebSocket enabled UIs.
The problem is the line
factory.protocol = GUIClientProtocol(frame)
This sets factory.protocol to an instance of GUIClientProtocol. But it needs to be the class.
Now, you apparently want to have frame accessible from within GUIClientProtocol. There are (at least) two options.
Option 1:
factory.protocol = GUIClientProtocol
factory._frame = frame
and then access as self.factory._frame from the (running) protocol instance.
Option 2: Implement Factory.buildProtocol.
A code posted on Redirecting Output in PyQt does two good things at once: it takes advantage of logging module to nicely format messages and it redirects standard stdout and stderr in to QT QTextBrowser widget.
But I would like QTextBrowser to receive all the print output coming out of running code. Particularly I want to redirect the nicely formatted messages that come from logger.
An ideal solution would re-direct every logger. output in to QTextBrowser (and not just stdout and stderr). As a matter of fact I would rather redirect logger's messages instead of stdout and stderr ones if I would have to make a choice between the twos....
So here are the commands used to printout formatted messages:
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
And here is the code:
========
import sys
from PyQt4 import QtCore, QtGui
import logging
logger = logging.getLogger(__name__)
class XStream(QtCore.QObject):
_stdout = None
_stderr = None
messageWritten = QtCore.pyqtSignal(str)
def flush( self ):
pass
def fileno( self ):
return -1
def write( self, msg ):
if ( not self.signalsBlocked() ):
self.messageWritten.emit(unicode(msg))
#staticmethod
def stdout():
if ( not XStream._stdout ):
XStream._stdout = XStream()
sys.stdout = XStream._stdout
return XStream._stdout
#staticmethod
def stderr():
if ( not XStream._stderr ):
XStream._stderr = XStream()
sys.stderr = XStream._stderr
return XStream._stderr
class MyDialog(QtGui.QDialog):
def __init__( self, parent = None ):
super(MyDialog, self).__init__(parent)
self._console = QtGui.QTextBrowser(self)
self._button = QtGui.QPushButton(self)
self._button.setText('Test Me')
layout = QtGui.QVBoxLayout()
layout.addWidget(self._console)
layout.addWidget(self._button)
self.setLayout(layout)
XStream.stdout().messageWritten.connect( self._console.insertPlainText )
XStream.stderr().messageWritten.connect( self._console.insertPlainText )
self._button.clicked.connect(self.test)
def test( self ):
print 'printing LINE 1'
print 'printing LINE 2'
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
# error out something
print blah
if ( __name__ == '__main__' ):
# logging.basicConfig()
# logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.basicConfig(level=logging.DEBUG)
app = None
if ( not QtGui.QApplication.instance() ):
app = QtGui.QApplication([])
dlg = MyDialog()
dlg.show()
if ( app ):
app.exec_()
POSTED LATER::FULLY WORKING EXAMPLE::SOLVED BY Mr.Dano
import sys
from PyQt4 import QtCore, QtGui
import logging
class QtHandler(logging.Handler):
def __init__(self):
logging.Handler.__init__(self)
def emit(self, record):
record = self.format(record)
if record: XStream.stdout().write('%s\n'%record)
# originally: XStream.stdout().write("{}\n".format(record))
logger = logging.getLogger(__name__)
handler = QtHandler()
handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
class XStream(QtCore.QObject):
_stdout = None
_stderr = None
messageWritten = QtCore.pyqtSignal(str)
def flush( self ):
pass
def fileno( self ):
return -1
def write( self, msg ):
if ( not self.signalsBlocked() ):
self.messageWritten.emit(unicode(msg))
#staticmethod
def stdout():
if ( not XStream._stdout ):
XStream._stdout = XStream()
sys.stdout = XStream._stdout
return XStream._stdout
#staticmethod
def stderr():
if ( not XStream._stderr ):
XStream._stderr = XStream()
sys.stderr = XStream._stderr
return XStream._stderr
class MyDialog(QtGui.QDialog):
def __init__( self, parent = None ):
super(MyDialog, self).__init__(parent)
self._console = QtGui.QTextBrowser(self)
self._button = QtGui.QPushButton(self)
self._button.setText('Test Me')
layout = QtGui.QVBoxLayout()
layout.addWidget(self._console)
layout.addWidget(self._button)
self.setLayout(layout)
XStream.stdout().messageWritten.connect( self._console.insertPlainText )
XStream.stderr().messageWritten.connect( self._console.insertPlainText )
self._button.clicked.connect(self.test)
def test( self ):
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
print 'Old school hand made print message'
if ( __name__ == '__main__' ):
app = None
if ( not QtGui.QApplication.instance() ):
app = QtGui.QApplication([])
dlg = MyDialog()
dlg.show()
if ( app ):
app.exec_()
You can create a custom logging.Handler and add it to your logger:
import logging
logger = logging.getLogger(__name__)
class QtHandler(logging.Handler):
def __init__(self):
logging.Handler.__init__(self)
def emit(self, record):
record = self.format(record)
XStream.stdout().write("{}\n".format(record))
handler = QtHandler()
handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
Then remove the logging.basisConfig(level=logging.DEBUG) line in the if __name__ == "__main__": block. You'll see your log messages only appear in your dialog box.
The answer given by dano works for 2.7.x, but not for 3.x.
To get the code provided by #dano working in 3.4.3 I had to make the obvious changes to the print statements and also change the write() method in the XStream class from self.messageWritten.emit(unicode(msg)) to self.messageWritten.emit(msg). That unicode call just made the dialog sit there and stare back at me in amusement.