I have a simple Tkinter, Matplotlib python program. I am using Windows 10 and the program works fine until I close the window. Upon restarting the script I recieve a runtime error (see image). I tried to "contact the application's support team for more information" then I realized that I am the application support team....
#!/usr/bin/env python
import matplotlib
import time
matplotlib.use('TkAgg')
from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from tkFileDialog import *
import sys
import Tkinter as Tk
def destroy(e):
sys.exit()
class Window():
def __init__(self):
self.root = Tk.Tk()
self.root.wm_title("Embedding in TK")
self.QuitButton = None
self.UploadButton = None
self.filepath = None
self.fig = None
self.canvas = None
self.image = None
def showWindow(self):
self.root.mainloop()
def addQuitButton(self, ButtonText):
if self.QuitButton is None:
self.QuitButton = Tk.Button(master = self.root, text = ButtonText, command = self.closeWindow)
self.QuitButton.pack(side=Tk.BOTTOM)
def addUploadButton(self, ButtonText):
if self.UploadButton is None:
self.UploadButton = Tk.Button(master = self.root, text = ButtonText, command = self.showFileDialog)
self.UploadButton.pack(side=Tk.BOTTOM)
def showFileDialog(self):
print 'oh yeah'
if self.filepath is None:
print 'Oh no...'
self.filepath = askopenfilename(parent=self.root)
print self.filepath
self.image = mpimg.imread(self.filepath)
self.buildFigure()
self.showCanvas()
def buildFigure(self):
self.fig = plt.figure()
self.im = plt.imshow(self.image)
def showCanvas(self):
self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def closeWindow(self):
#sys.exit()
self.root.quit()
self.root.destroy()
if __name__== "__main__":
base = Window()
base.addQuitButton("Quit")
base.addUploadButton("Upload")
base.showWindow()
I found that Idle was the problem, though I don't know why. When I run my script in command line it has no problem.
Related
I'm a little stuck here. I've read lots of stack overflows threads but not getting any further on that topic.
My goal is to have a tinter GUI that at some point launches a function in a new Process and redirects every print in that function to the Guis Text widget. There a Pipes and Queues but im not to familiar with how to use them correctly. I have found a working solution here but that only applies to Python 3. Unfortunately I have to use Python 2.7...
Can anybody help?
my sample code:
from Tkinter import *
import multiprocessing as mp
import time
import sys
class Gui(object):
def __init__(self):
self.a=Tk()
b1=Button(self.a, text="Process 1", command=self.func)
b1.grid(row=0, column=0, pady=10, padx=10, sticky=SE)
self.messages=Text(
self.a, height=2.5, width=30, bg="light cyan", state=NORMAL)
self.messages.grid(row=1, column=0, columnspan=3)
sys.stdout = self.StdoutRedirector(self.messages)
sys.stderr = self.StdoutRedirector(self.messages)
self.a.mainloop()
class StdoutRedirector(object):
def __init__(self, text_widget):
self.output = text_widget
def write(self, string):
self.output.config(state=NORMAL)
self.output.update_idletasks()
self.output.insert('end', string)
self.output.see('end')
self.output.config(state=DISABLED)
def flush(self):
pass
def func(self):
print("test")
proc=mp.Process(target=go)
proc.start()
def go():
for i in range(0,10):
time.sleep((1))
print(i)
if __name__ == "__main__":
Gui()
The issue in your code is that you are changing the content of a tkinter widget outside of the mainloop, which is not possible. One solution I have found is to redirect the stdout and stderr to Queues and periodically read the content of these queues in the mainloop to update the text widget. The periodical reading is done using the method .after(<delay in ms>, <callback>).
from Tkinter import *
import multiprocessing as mp
import time
import sys
class StdoutRedirector(object):
def __init__(self, queue):
self.output = queue
def write(self, string):
self.output.put(string)
def flush(self):
pass
class Gui(object):
def __init__(self):
self.a=Tk()
b1 = Button(self.a, text="Process 1", command=self.func)
b1.grid(row=0, column=0, pady=10, padx=10, sticky=SE)
self.messages=Text(self.a, height=2.5, width=30, bg="light cyan",
state=NORMAL)
self.messages.grid(row=1, column=0, columnspan=3)
self.messages.tag_configure('error', foreground='red')
# queues to get the stdout and stderr
self.stdout = mp.Queue()
self.stderr = mp.Queue()
self.proc = None # process
self.a.mainloop()
def stdout_update(self):
self.messages.configure(state=NORMAL)
while not self.stdout.empty(): # display stdout
self.messages.insert('end', self.stdout.get(False))
while not self.stderr.empty(): # display stderr
self.messages.insert('end', self.stderr.get(False), 'error')
self.messages.configure(state=DISABLED)
if self.proc.is_alive(): # the process is not finished, schedule the next display update
self.a.after(100, self.stdout_update)
def func(self):
self.proc = mp.Process(target=go, args=(self.stdout, self.stderr))
self.proc.start()
self.stdout_update() # launch display update
def go(queue_out, queue_err):
# redirect stdout and stderr to queues
sys.stdout = StdoutRedirector(queue_out)
sys.stderr = StdoutRedirector(queue_err)
for i in range(0, 2):
time.sleep(1)
print(i)
1/0 # to test the stderr
if __name__ == "__main__":
Gui()
I use django, celery, scrapy.
My settings for celery:
CELERY_BROKER_URL = 'amqp://****/myvhost'
CELERY_TIMEZONE = TIME_ZONE
CELERYD_CONCURRENCY = 1000
CELERYD_MAX_TASKS_PER_CHILD = 4
CELERY_IGNORE_RESULT = True
# django celery
CELERY_RESULT_BACKEND = 'django-db'
# celery queues setup
CELERY_DEFAULT_QUEUE = 'default'
CELERY_DEFAULT_ROUTING_KEY = 'default'
CELERY_QUEUES = (
Queue('get_context', Exchange('get_context'), routing_key='get_context'),
Queue('get_article', Exchange('get_article'), routing_key='get_article'),
)
CELERY_ROUTES = {
'parse.tasks.get_context': {
'queue': 'get_context',
'routing_key': 'get_context',
},
'parse.tasks.get_article': {
'queue': 'get_article',
'routing_key': 'get_article',
},
}
There are two tasks on celery:
from api_parser import celery_app
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from scrapy_parser.scrapy_parser.spiders.map_links import MapLinksSpider
from scrapy_parser.scrapy_parser.spiders.articles import ArticlesSpider
from threading import Thread
#celery_app.task
def get_context(rules_id, rules):
process = CrawlerProcess(get_project_settings())
process.crawl(MapLinksSpider, rules_id=rules_id, rules=rules)
Thread(target=process.start).start()
#celery_app.task
def get_article(rules_id, link_id, rules, link):
process = CrawlerProcess(get_project_settings())
process.crawl(ArticlesSpider, rules_id=rules_id, link_id=link_id, rules=rules, link=link)
Thread(target=process.start).start()
The first task is triggered by a signal and maps the links.
The second task is started when a new link is added to the database.
My signals in django:
from django.db.models.signals import post_save
from django.dispatch import receiver
from parse.models.rules import Scheduler, Rules, ParseLinks
from parse.tasks import get_context, get_article
#receiver(post_save, sender=Scheduler)
def create_task_get_context(sender, instance, created, **kwargs):
if created:
rules = Rules.objects.get(id=int(instance.rules.id))
get_context.delay(int(rules.id), str(rules.rules))
#receiver(post_save, sender=ParseLinks)
def create_task_get_article(sender, instance, created, **kwargs):
if created:
parse_link = ParseLinks.objects.get(id=int(instance.id))
get_article.delay(int(parse_link.rules.id), int(parse_link.id), str(parse_link.rules.rules), str(parse_link.link))
My spiders:
map_links.py
from parse.models.rules import ParseLinks
import scrapy
import json
class MapLinksSpider(scrapy.Spider):
name = "map_links"
start_urls = []
def __init__(self, **kw):
super(MapLinksSpider, self).__init__(**kw)
self.rules_id = kw.get('rules_id')
self.rules = json.loads(kw.get('rules'))
self.start_urls = [self.rules['url']]
self.templates = self.rules['item']['templates']
self.pagination = self.rules['pagination']
def parse(self, response):
for item in self.templates:
context = response.css(str(item['context']))
for row in context:
link = row.css('%s::attr(%s)' % (item['link']['cssSelector'], item['link']['attr'])).extract_first(),
title = row.css('%s::text' % item['options']['title']['cssSelector']).extract_first(),
date = row.css('%s::text' % item['options']['date']['cssSelector']).extract_first()
ParseLinks.objects.get_or_create(rules_id=self.rules_id, link=self.rules['url'] + link[0], title=title, date=date)
next_page = response.css('%s::attr(%s)' % (self.pagination['link']['cssSelector'], self.pagination['link']['attr'])).extract_first()
if next_page is not None:
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
articles.py
from parse.models.rules import ParseData
import scrapy
import json
class ArticlesSpider(scrapy.Spider):
name = "articles"
start_urls = []
def __init__(self, **kw):
super(ArticlesSpider, self).__init__(**kw)
self.rules_id = kw.get('rules_id')
self.link_id = kw.get('link_id')
self.rules = json.loads(kw.get('rules'))
self.link = kw.get('link')
def parse(self, response):
self.start_urls = [self.link]
title = response.css('%s::text' % self.rules['article']['title']['cssSelector']).extract_first()
text = response.css('%s::text' % self.rules['article']['text']['cssSelector']).extract_first()
ParseData.objects.create(rules_id=self.rules_id, link_id=self.link_id, title=title, text=text)
yield {
"title": title,
'text': text
}
But I get the error: twisted.internet.error.ReactorNotRestartable
I understand that the error is caused by the launch of a new process for the spider. But I'm using threads. And I do not understand why this does not solve my problem.
I think every beginning scraper meets this question :)
Try this:
0) pip install crochet
import from crochet import setup
setup() - at the top of the file
remove 2 lines:
a) d.addBoth(lambda _: reactor.stop())
b) reactor.run()
The only meaningful lines from [Scrapy docs][2] left are 2 last lines in this my code:
#some more imports
from crochet import setup
setup()
def run_spider(spiderName):
module_name="first_scrapy.spiders.{}".format(spiderName)
scrapy_var = import_module(module_name) #do some dynamic import of selected spider
spiderObj=scrapy_var.mySpider() #get mySpider-object from spider module
crawler = CrawlerRunner(get_project_settings()) #from Scrapy docs
crawler.crawl(spiderObj) #from Scrapy docs
This code allows to select what spider to run just with its name passed to run_spider function and after scraping finishes - select another spider and run it again.
Next you simply run run_spider from Celery task.
[1]: ReactorNotRestartable - Twisted and scrapy
[2]: https://doc.scrapy.org/en/latest/topics/practices.html
I have a class called app.py within that class there's a method called print_raw_records_screen here's the part of the class and the method
app.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Tkinter import *
from ttk import *
import os
import mftsession
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.filename = ""
self.initUI()
#defining a function that that receives the raw records from the mftsession.py and print it to the screen
#this script will be called by the mftsession.py in
#process_mftfile()
def print_raw_records_screen(self,records):
self.area.delete(1.0, "end")
self.area.insert('1.0',records)
def initUI(self):
self.parent.title("Mtf Analyzer")
#initializing and configuring menubar
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="Open file", command=self.fileOpen)
fileMenu.add_command(label="Exit", command=self.onExit)
menubar.add_cascade(label="File", menu=fileMenu)
#specify grid row and column spaces
self.pack(fill=BOTH, expand=True)
self.columnconfigure(1, weight=1)
self.columnconfigure(3, pad=7)
self.rowconfigure(3, weight=1)
self.rowconfigure(5, pad=7)
lbl = Label(self, text="File Name")
lbl.grid(row=1, column=0, sticky=W, pady=4, padx=5)
self.filename_area = Entry(self)
self.filename_area.grid(row=1, column=1, columnspan=5, padx=5, sticky=E+W+S+N)
analize_button = Button(self, text="Analize", command=self.processFile)
analize_button.grid(row=1, column=6, padx=5)
self.area = Text(self)
self.area.grid(row=2, column=1, columnspan=2, rowspan=4,
padx=5, sticky=E+W+S+N)
#configure the raw output view
def onExit(self):
self.quit()
#this function selects and opens the file to analize
def fileOpen(self):
from tkFileDialog import askopenfilename
Tk().withdraw()
self.filename = askopenfilename()
#populate the filename field
self.set( self.filename)
#do the processing of the file obtained. Populate the file NAME entry or
#send the filename to the analyzeMFT.py
def processFile(self):
arguments = "analyzeMFT.py -f "+self.filename+" -d --bodyfull -l -o "+self.filename+".csv"
os.system(arguments)
mftsession.MftSession.process_mft_file(self)
#get and set methods for the entry field
def get(self):
return self.filename_area.get()
def set(self, value):
self.filename_area.delete(0, END)
self.filename_area.insert(0,value)
def main():
root = Tk()
root.geometry("450x350+500+500")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
This method is called by another external script like this
mftsession.py
import csv
import json
import os
import sys
from optparse import OptionParser
import mft
import app
from Tkinter import *
from ttk import *
class MftSession:
"""Class to describe an entire MFT processing session"""
#staticmethod
def fmt_excel(date_str):
return '="{}"'.format(date_str)
#staticmethod
def fmt_norm(date_str):
return date_str
def __init__(self):
self.mft = {}
self.fullmft = {}
self.folders = {}
self.debug = False
self.mftsize = 0
def process_mft_file(self):
root = Tk()
appi = app.Example(root)
self.sizecheck()
self.build_filepaths()
# reset the file reading
self.num_records = 0
self.file_mft.seek(0)
raw_record = self.file_mft.read(1024)
if self.options.output is not None:
self.file_csv.writerow(mft.mft_to_csv(None, True, self.options))
while raw_record != "":
record = mft.parse_record(raw_record, self.options)
if self.options.debug:
print record
appi.print_raw_records_screen(raw_record ) #THIS FUNCTION WHILE INVOKED THIS WAY IS NOT WORKING
..........
The script analyzeMFT.py called by the app.py through the subrouting
#!/usr/bin/python
import sys
from os import path
def main():
session = mftsession.MftSession()
session.mft_options()
session.open_files()
session.process_mft_file()
if __name__ == '__main__':
if __package__ is None:
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
import mftsession
main()
else:
import mftsession
main()
When called this way it prints nothing to the window, but when i invoke it for testing within its own class it prints the test
def print_raw_records_screen(self,records):
self.area.delete(1.0, "end")
self.area.insert('1.0', records)
and invoke like print_raw_records_screen("any test"): in the app.py
An image can tell alot. and summarize
What am i doing wrong? I sense it's the instantiation am doing wrong. I need diretions please
From what I can see here you have a few issues causing problems.
you are importing app.py on mftsession.py instead importing mftsession.py on app.py.
You are trying to use appi.print_raw_records_screen(raw_record) on a completely different instance of the Example() class with appi = app.Example(root) remove that part all together.
It is bad practice to importing inside a function. Import at the start of each py file.
There is so many things going on in your code I had to create a Minimal, Complete, and Verifiable example example of my own to illustrate the relation between files.
Here is a simple example of how the 2 files can interact and the way I think you are trying to do things.
Here I have created a main file called app.py:
from Tkinter import *
# You might need to import the py file with the package name as well.
# Change the package name to the package your python files are located in.
import PACKAGE_NAME.file_b
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.filename = ""
analize_button = Button(self.parent, text="Analize", command=self.processFile)
analize_button.pack()
self.area = Text(self.parent, height = 2, width = 40)
self.area.pack()
def print_raw_records_screen(self,records):
self.area.delete(1.0, "end")
self.area.insert('1.0',records)
def processFile(self):
PACKAGE_NAME.file_b.process_mft_file(self)
if __name__ == "__main__":
root = Tk()
app = Example(root)
root.mainloop()
Here I have created a file called file_b.py
from Tkinter import *
def process_mft_file(self):
record = "Some values assigned to a variable"
self.print_raw_records_screen(record)
The results look something like this:
I think my issue is simple, but I have hit a brick wall. I am not a programmer but needed a program to control a laser engraver via Arduino. My Original code was mostly working, but I wanted the ability to select a serial port with a QComboBox so I can make it software available for everyone.
I figured out how to do that with the code below:
import sys
import serial
import time
import serial.tools.list_ports
from PyQt4 import QtGui
from window_test import Ui_MainWindow
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btn_laser_poweron.clicked.connect(self.btnFIRE)
self.ui.btn_laser_poweroff.clicked.connect(self.btnOFF)
self.ui.btn_lig_power.clicked.connect(self.btnLIG)
self.ui.btn_cutting_power.clicked.connect(self.btnCUT)
self.ui.btn_power_meter.clicked.connect(self.btnTEST)
self.ui.spinBox.valueChanged.connect(self.PwrLevel)
self.ui.comboBox.activated.connect(self.srlprt)
def srlprt(self):
serial.Serial(str(self.ui.comboBox.currentText()))
def btnFIRE(self):
ser.write("a" + chr(255))
def btnOFF(self):
ser.write("b" + chr(0))
def btnTEST(self):
ser.write("c" + chr(0))
time.sleep(59.5)
ser.write("d" + chr(255))
def btnLIG(self):
ser.write("e" + chr(29))
def btnCUT(self):
ser.write("f" + chr(160))
def PwrLevel(self):
val = self.ui.spinBox.value()
ser.write("g" + chr(val))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
Now my problem is that none of my buttons work because "ser" is not globally defined. I understand that I broke that when I removed "ser = serial.Serial(port=COM3)" when it was above the class definition, but I don't know how to fix it. Any help would be greatly appreciated.
Cheers!
A simple solution would be to just set ser as attribute of your Main instance. Also it couldn't hurt to close the serial connection if it is open before opening a new one, e.g:
import sys
import serial
import time
import serial.tools.list_ports
from PyQt4 import QtGui
from window_test import Ui_MainWindow
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ser = None
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btn_laser_poweron.clicked.connect(self.btnFIRE)
self.ui.btn_laser_poweroff.clicked.connect(self.btnOFF)
self.ui.btn_lig_power.clicked.connect(self.btnLIG)
self.ui.btn_cutting_power.clicked.connect(self.btnCUT)
self.ui.btn_power_meter.clicked.connect(self.btnTEST)
self.ui.spinBox.valueChanged.connect(self.PwrLevel)
self.ui.comboBox.activated.connect(self.srlprt)
def srlprt(self):
if self.ser:
self.ser.close()
self.ser = serial.Serial(str(self.ui.comboBox.currentText()))
def btnFIRE(self):
self.ser.write("a" + chr(255))
def btnOFF(self):
self.ser.write("b" + chr(0))
def btnTEST(self):
self.ser.write("c" + chr(0))
time.sleep(59.5)
self.ser.write("d" + chr(255))
def btnLIG(self):
self.ser.write("e" + chr(29))
def btnCUT(self):
self.ser.write("f" + chr(160))
def PwrLevel(self):
val = self.ui.spinBox.value()
self.ser.write("g" + chr(val))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
I wrote a simple application in Python and PySide. When I run it, SIGNALs are not working.
The application starts without errors.
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
dial = QDial()
dial.setNotchesVisible(True)
spinbox = QSpinBox()
layout = QHBoxLayout()
layout.addWidget(dial)
layout.addWidget(spinbox)
self.setLayout(layout)
self.connect(dial, SIGNAL("valueChaged(int)"), spinbox.setValue)
self.connect(spinbox, SIGNAL("valueChaged(int)"), dial.setValue)
self.setWindowTitle("Signals and Slots")
# END def __init__
# END class Form
def main():
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
# END def main
if __name__ == '__main__':
main()
# END if
I am using:
Pyside 1.2.2;
Python 2.7.6;
OS Centos; Windows 7
I am running the application with:
Sublime Text 3 and Eclipse Luna;
How can I make SIGNALs working?
Your signal name is incorrect;
Incorrect :
valueChaged (int)
Correct :
valueChanged (int)
Test it, work fine;
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class QFormDialog (QDialog):
def __init__(self, parent = None):
super(QFormDialog, self).__init__(parent)
self.myQial = QDial()
self.myQSpinbox = QSpinBox()
self.myQHBoxLayout = QHBoxLayout()
self.myQial.setNotchesVisible(True)
self.myQHBoxLayout.addWidget(self.myQial)
self.myQHBoxLayout.addWidget(self.myQSpinbox)
self.setLayout(self.myQHBoxLayout)
self.connect(self.myQial, SIGNAL('valueChanged(int)'), self.myQSpinbox.setValue)
self.connect(self.myQSpinbox, SIGNAL('valueChanged(int)'), self.myQial.setValue)
self.setWindowTitle('Signals and Slots')
if __name__ == '__main__':
myQApplication = QApplication(sys.argv)
myQFormDialog = QFormDialog()
myQFormDialog.show()
myQApplication.exec_()
Note : PyQt4 & PySide is same way to implemented.