I'm trying to make a little pinging tool with Scapy and PyQt4.
The code is fairly simple and all it does now is pinging an address the user can type in.
from PyQt4 import QtGui
import sys
from scapy.all import *
from scapy.sendrecv import sr, send
def q2s(qstr): return "%s" %qstr
class Application(QtGui.QMainWindow):
def __init__(self):
super(Application, self).__init__()
self.resize(1000,500)
self.centre()
self.initGui()
self.show()
def initGui(self):
self.ipAddress = QtGui.QLineEdit("1.1.1.1",self)
self.label = QtGui.QLabel("...")
self.label.move(50,100)
pingBtn = QtGui.QPushButton("Ping!", self)
pingBtn.move(50,50)
pingBtn.clicked.connect(self.ping)
def ping(self):
ip = q2s(self.ipAddress.text())
ans, unans = sr(IP(dst=ip)/ICMP(), timeout=1, verbose=0)
if ans:
self.label.setText("Host is up")
else:
self.label.setText("Host is down")
def centre(self):
screen = QtGui.QDesktopWidget().screenGeometry()
sizeNow = self.geometry()
self.move((screen.width() - sizeNow.width()) / 2,
(screen.height() - sizeNow.height()) / 2)
def run():
app = QtGui.QApplication(sys.argv)
GUI = Application()
sys.exit(app.exec_())
run()
However, when trying to ping an IP address an error is printed to the console.
Traceback (most recent call last):
File "Application.py", line 71, in ping
ans, unans = sr(IP(dst=ip)/ICMP(), timeout=1, verbose=0)
File "/usr/lib/python2.7/dist-packages/scapy/sendrecv.py", line 317, in sr
a,b=sndrcv(s,x,*args,**kargs)
File "/usr/lib/python2.7/dist-packages/scapy/sendrecv.py", line 129, in sndrcv
inp, out, err = select(inmask,[],[], remaintime)
select.error: (4, 'Unterbrechung w\xc3\xa4hrend des Betriebssystemaufrufs')
The last line means something like "Interruption during call of operating system".
I can not see what might be wrong about the program.
Using the send function instead of the sr function works somehow. So I think the problem might be that the application is waiting for a response. But I still don't know how to fix the error.
It's a bug in Scapy: an exception we want to catch when calling select() is different when in multi-thread environment.
A pull-request (#417) is currently examined (I'll update this answer when it has been merged), so you can check if the patch fixes this issue for you (I hope so!).
Related
The Problem
I'm working on a Python GUI, using Tkinter. I'm also trying to add "toaster" messages, using Ptoaster. Here's an example of what I'm trying to achieve:
from tkinter import *
import ptoaster
PADDING = 20
class MyInterface:
def __init__(self):
self.root = Tk()
self.label = self.make_label()
print_welcome()
def make_label(self):
result = Label(self.root, text="Hello, world!")
result.pack(padx=PADDING, pady=PADDING)
return result
def run_me(self):
self.root.mainloop()
def print_welcome():
message = "Hello again!"
ptoaster.notify("Hello!", message)
interface = MyInterface()
interface.run_me()
If I try to run the above code, one of two things will happen:
The command line will spit out the following error:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python3: ../../src/xcb_io.c:260: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
XIO: fatal IO error 25 (Inappropriate ioctl for device) on X server ":0"
after 207 requests (207 known processed) with 2 events remaining.
Aborted (core dumped)
My whole laptop will freeze, necessitating a hard reset.
However, if I move the call print_welcome() from outside of MyInterface, so that it's called before this class is initialised, then none of the above errors crop up.
What I'd Like to Know
How to call a function, from within a Tkinter GUI class, which causes a "toaster" message to be displayed, without causing the whole platform to crash.
Why the above errors are cropping up.
Documentation states it needs to be verified that the ptoaster.notify is called from the main program.
IMPORTANT - you need to make sure you call notify from the main program
Working code for me:
from tkinter import *
import ptoaster
PADDING = 20
class MyInterface:
def __init__(self):
self.root = Tk()
self.label = self.make_label()
print_welcome()
def make_label(self):
result = Label(self.root, text="Hello, world!")
result.pack(padx=PADDING, pady=PADDING)
return result
def run_me(self):
self.root.mainloop()
def print_welcome():
message = "Hello again!"
ptoaster.notify("Hello!", message)
if __name__ == '__main__':
interface = MyInterface()
interface.run_me()
Documentation (See: Sample Program)
The full traceback for my error is:
> python zthreadtest_tjedit.py
None
Traceback (most recent call last):
File "zthreadtest_tjedit.py", line 17, in run self.function()
TypeError: 'list' object is not callable
I apologize that's this code is a bit system specific and probably won't serve as an executable example for most people. Hopefully the solution is simple enough for someone to see right off. If you're not running zfs with a currently imported zpool, but are on a *nix platform with the weir.zfs module installed, it will return an empty list with the non-threaded code enabled (see comments in code for toggling threading). With the threading module enabled, it throws the error as shown above.
Confusing to me is that the second snip of code from Jo Plaete (https://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/) runs without error, and I simply modified this code to my needs.
EDIT: This error-causing difference may be related to the list object used in my code and not hers, but I still need to make mine work.
My question is: how do I resolve my error such that my threaded module runs correctly?
This seems simple, but I'm absolutely stumped. So much so that this is the first question I've posted on any help forum ever! I hope I've asked my question properly and I appreciate any help.
My Code, from much larger pyside gui program:
import PySide, sys
from PySide import QtCore, QtGui
from PySide.QtCore import *
from PySide.QtGui import *
import re, subprocess, threading
from weir import zfs
class WorkerThread(QThread):
def __init__(self, function):
QThread.__init__(self)
self.function = function
def __del__(self):
self.wait()
def run(self):
self.function()
return
class MainZ(QMainWindow):
def __init__(self):
super(MainZ, self).__init__()
# print(self)
# imported_pools = self.get_imported() # No threading
imported_pools = self.thread_test() # Use threading module
print(imported_pools)
def thread_test(self):
self.threader = WorkerThread(self.get_imported())
self.threader.start()
def get_imported(self):
pool_string = subprocess.getoutput(
'zpool list |grep -v ^NAME.*SIZE.*ALLOC |grep -o ^[a-Z0-9]*')
imported_pools = re.split(r'\s *', pool_string)
return imported_pools
app = QApplication(sys.argv)
form = MainZ()
app.exec_()
Code I modeled from Jo Plaete that works for me without error:
import sys, time
from PySide import QtCore, QtGui
class GenericThread(QtCore.QThread):
def __init__(self, function, *args, **kwargs):
QtCore.QThread.__init__(self)
self.function = function
self.args = args
self.kwargs = kwargs
def __del__(self):
self.wait()
def run(self):
self.function(*self.args,**self.kwargs)
return
class MyApp(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 280, 600)
self.setWindowTitle('threads')
self.layout = QtGui.QVBoxLayout(self)
self.testButton = QtGui.QPushButton("test")
self.connect(self.testButton, QtCore.SIGNAL("released()"), self.test)
self.listwidget = QtGui.QListWidget(self)
self.layout.addWidget(self.testButton)
self.layout.addWidget(self.listwidget)
def add(self, text):
""" Add item to list widget """
print("Add: " + text)
self.listwidget.addItem(text)
self.listwidget.sortItems()
def addBatch(self,text="test",iters= 5,delay=0.2):
""" Add several items to list widget """
for i in range(iters):
time.sleep(delay) # artificial time delay
self.add(text+" "+str(i))
def test(self):
self.listwidget.clear()
self.genericThread = GenericThread(
self.addBatch,"from generic thread ",delay=0.3)
self.genericThread.start()
# run
app = QtGui.QApplication(sys.argv)
test = MyApp()
test.show()
app.exec_()
The
self.threader = WorkerThread(self.get_imported())
should read
self.threader = WorkerThread(self.get_imported)
When creating the thread, you want to pass the function itself and not the result of calling the function (which is a list).
an update to my code, based on the reply of Israel Unterman:
The Error-Class is now
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow
class Error(QtWidgets.QMainWindow):
reply = False
last_reply_id = None
last_id = 0
def __init__(self, error_code_string, parent=None):
super().__init__(parent)
QtWidgets.QMessageBox.warning(self, "Warnung", error_code_string, QtWidgets.QMessageBox.Ok)
id = give_id(self)
def give_id(self):
self.last_id += 1
return self.last_id
def give_reply(self):
if last_id == last_reply_id:
return self.reply
else:
return None
def set_reply(self, button, id):
if button in (QMessageBox.Ok, QMessageBox.Yes):
reply = True
else:
reply = False
self.last_reply_id = id
return reply
And the Test-Script comes with
from ErrorHandling import Error
Error('Test')
If I am using the normal Code (practically the same Code, just wrapped in a class) the message appears and then at the line
id = give_id(self)
the Code stops without any errormessage from python, just:
Process finished with exit code 1
If I use the test-script, there is nothing (No QMessageBox!) than this:
Process finished with exit code 1
If I debug the Code, init() gets the same Objects and variables, but
super().__init__(parent)
fails without any message.
So where is the mistake, or difference.
Here a shortend Version of the Class (it's too long to show all code here), from which the "Error" works nearly fine:
from ErrorHandling import Error
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# set some variables
self.create_layout()
def create_layout(self):
# creates a GUI using QWidgets with some Inputboxes and one button
[...]
def button_start_clicked(self):
Error('Check the input')
Here is the old question:
I have a problem regarding the setup of QtWidgets.QMessageBox.
All Code follows the description.
The ErrorHandling-Modul should give a message about an error.
If needed it may ask a question too.
The function ErrorMsg.errorMessage is called from other Moduls in case of an catched exception.
There will be more functions added.
If I run the code the following error occurs:
Connected to pydev debugger (build 145.1504)
Traceback (most recent call last):
File "C:\Program Files (x86)\JetBrains\PyCharm Community Edition 2016.1.4\helpers\pydev\pydevd.py", line 1531, in <module>
globals = debugger.run(setup['file'], None, None, is_module)
File "C:\Program Files (x86)\JetBrains\PyCharm Community Edition 2016.1.4\helpers\pydev\pydevd.py", line 938, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:\Program Files (x86)\JetBrains\PyCharm Community Edition 2016.1.4\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "C:/Quellcode/AllgTest.py", line 5, in <module>
reply = errm.errorMessage('Test')
File "C:/Quellcode\ErrorHandling.py", line 20, in errorMessage
msg_box.setIcon(QMessageBox.Warning)
TypeError: QMessageBox.setIcon(QMessageBox.Icon): first argument of unbound method must have type 'QMessageBox'
Process finished with exit code 1
I tried quite some variations and googled, but I have no idea what the problem is since I found some examples that are using the line
QMessageBox.setIcon(QMessageBox.Icon)
So where is my mistake?
And now the Code:
There is the following testscript to test my ErrorMsg-class
from ErrorHandling import ErrorMsg
errm = ErrorMsg()
reply = errm.errorMessage('Test')
And here is my ErrorHandling-Modul
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import QMainWindow
class ErrorMsg(QMainWindow):
def __init__(self):
pass
def giveback(self,button):
if button in (QMessageBox.Ok, QMessageBox.Yes):
reply = True
else:
reply = False
return reply
def errorMessage(self, error_msg, buttons='OK'):
msg_box = QMessageBox
msg_box.setIcon(QMessageBox.Warning)
msg_box.setWindowTitle('Warning')
if buttons == 'OK':
msg_box.setStandardButtons(QMessageBox.Ok)
elif buttons == 'YesNo':
msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
else:
error_msg = 'Unknown Button >' + buttons + '<, use >OK< or >YesNo<'
raise ValueError(error_msg)
msg_box.setText(error_msg)
clicked_button = msg_box.exec()
return giveback(clicked_button)
Thanks for your help
James
You didn't create an object of the message box. To create an object use:
msg_box = QMessageBox()
But you don'y need to go through all this, since QMessageBox has static functions for showing messages, which you can call directly on the QMessageBox class. For example:
QMessageBox.warning(None, 'title', 'msg')
You also have some control over the butons, see QMessageBox
I have googled so much about this but did not find anything..
I have a custom exception class which invoked a QDialog to show the warning message..Now i am writing test for my application and in the test, i am sending the data which will make the application raise that exception and hence the dialog will appear.
But when i am running the test using pt.test, the dialog is appearing and when i close it, this is printed on my console..
E Failed: DID NOT RAISE
apptest.py:31: Failed
----------------------------- Captured stderr call -----------------------------
Traceback (most recent call last):
File "/home/kapil/mp3wav/mp3wav/application.py", line 62, in <lambda>
self.connect(self.conversionButton, SIGNAL("clicked()"), lambda: self.startConversion())
File "/home/kapil/mp3wav/mp3wav/application.py", line 79, in startConversion
self.__convert()
File "/home/kapil/mp3wav/mp3wav/application.py", line 86, in __convert
self.checkFile(__OUTFILE)
File "/home/kapil/mp3wav/mp3wav/application.py", line 105, in checkFile
raise FileTypeException(fileType, self)
mp3wav.exceptions.fileexception.FileTypeException
This is my test file..
import pytest
import os
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from mp3wav.application import Mp3WavApp
from mp3wav.exceptions.fileexception import FileTypeException
from mp3wav.exceptions.libraryexception import LibraryException
from mp3wav.exceptions.filenotexistexception import FileNotExistException
from mp3wav.exceptions.samefileexception import SameFileException
from mp3wav.exceptions.overwriteexception import OverWriteException
def windowTest(qtbot):
testapp = Mp3WavApp()
testapp.show()
qtbot.addWidget(testapp)
assert testapp.isVisible()
assert testapp.close()
#pytest.mark.qt_no_exception_capture
def fileTypeTest(qtbot, tmpdir):
testapp = Mp3WavApp()
qtbot.addWidget(testapp)
infile = tmpdir.mkdir("files").join("demo.mp3")
infile.write("something")
testapp.inputFileLine.setText(str(tmpdir.join("files", "demo.mp3")))
testapp.outputFileLine.setText(str(tmpdir.join('files')))
testapp.outputFileLineName.setText('demo.wave')
with pytest.raises(FileTypeException) :
qtbot.mouseClick(testapp.conversionButton, Qt.LeftButton)
Here is the custom exception
import os
import sys
from mp3wav.exceptions.errordialog import ErrorDialog
class FileTypeException(Exception):
def __init__(self, sid, parent=None):
super(FileTypeException, self).__init__()
filetype = "mp3"
if sid==2:
filetype = "wav"
errorMessage = "The Selected file is not "+filetype+\
"\nSelect the appropriate file"
if parent:
dialog = ErrorDialog(parent, errorMessage)
dialog.show()
dialog.exec_()
else:
print(errorMessage)
What i think is that it is failing because the dialog is appearing during the run of my test and i need to close it manually..But i don't know exactly why the test is failing..
Can anyone please guide me to the right path.. It will very much be appreciated..
I had written a simple serial controller using serial--actually, it is more Frankstein code, pieces others have written and I pieced together. Feel free to poke fun at my ineptness, but any direction would be appreciated. It runs fine on my Linux running Backtrack, Python 2.6, but when I try to run it on the Raspberry Pi, Python 2.7, I get these errors:
Traceback (most recent call last):Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 504, in run
self.__target(*self.__args, **self.__kwargs)
File "/home/pi/Zombie Python 1.2 (Modified for Pi) (from Adapt 1.7).py", line 147, in rx
self.ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1, bytesize=8, stopbits=1)
File "/usr/local/lib/python2.7/dist-packages/serial/serialutil.py", line 260, in __init__
self.open()
File "/usr/local/lib/python2.7/dist-packages/serial/serialposix.py", line 276, in open
raise SerialException("could not open port %s: %s" % (self._port, msg))
SerialException: could not open port /dev/ttyUSB0: [Errno 2] No such file or directory: '/dev/ttyUSB0'
File "/home/pi/Zombie Python 1.2 (Modified for Pi) (from Adapt 1.7).py", line 185, in <module>
client = ThreadedClient(root)
File "/home/pi/Zombie Python 1.2 (Modified for Pi) (from Adapt 1.7).py", line 113, in __init__
self.periodicCall()
File "/home/pi/Zombie Python 1.2 (Modified for Pi) (from Adapt 1.7).py", line 119, in periodicCall
self.gui.processIncoming()
AttributeError: GuiPart instance has no attribute 'processIncoming'
My code is:
import Tkinter
import time
import threading
import random
import Queue
import serial
import readline
#import xbee
import sys
x=""
class GuiPart:
def __init__(self, master, queue, endApplication):
self.sonar = Tkinter.StringVar() # Feeds sonar sensor data to label
self.lm = Tkinter.StringVar() # Feeds left motor speed to label
self.rm = Tkinter.StringVar() # Feeds right motor speed to label
self.queue = queue
# Set up the GUI
frame1 = Tkinter.Frame(master, bd=200) #Setup frame.
frame1.bind("<Key>", key) # Allow frame to handle keypresses.
frame1.focus_set() #Set focus of frame so that keypresses activate event.
frame1.pack() #Show it.
#Button
console = Tkinter.Button(frame1, text='Close', command=endApplication)
console.pack()
# Add more GUI stuff here
self.lm.set(0) # Initializes left motor label
self.rm.set(0) # Initializes right motor label
self.sonar.set(0) # Initializes sonar label
#Sonar label
sonarLbl = Tkinter.Label(frame1, textvariable=self.sonar)
sonarLbl.pack()
#Right motor label
rmLbl = Tkinter.Label(frame1, text="Left Motor Speed: ", textvariable=self.rm)
rmLbl.pack()
#Left motor label
lmLbl = Tkinter.Label(frame1, textvariable=self.lm)
lmLbl.pack()
def key(self, event):
#print "pressed", repr(event.char)
#self.sonar = repr(event.char) <------ This should be the line to handle keypresses
global x
x = repr(event.char)
def processIncoming(self):
"""
Handle all the messages currently in the queue (if any).
"""
while self.queue.qsize():
try:
msg = self.queue.get(0)
# Check contents of message and do what it says
# As a test, we simply print it
# Below is where I will parse the "msg" variable, splitting
# it (msg.rsplit) to pull out sensor data and update labels.
lm, rm, sonar, mknt = msg.rsplit(",")
lm = "Left Motor Speed: "+lm
rm = "Right Motor Speed: "+rm
sonar = "Sonar: "+sonar+" CMs away"
self.sonar.set(sonar) # Setting the labels with latest sensor info.
self.lm.set(lm) # Setting the labels with latest sensor info.
self.rm.set(rm) # Setting the labels with latest sensor info.
except Queue.Empty:
pass
class ThreadedClient:
"""
Launch the main part of the GUI and the worker thread. periodicCall and
endApplication could reside in the GUI part, but putting them here
means that you have all the thread controls in a single place.
"""
def __init__(self, master):
"""
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI. We spawn a new thread for the worker.
"""
self.master = master
# Create the queue
self.queue = Queue.Queue()
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.endApplication)
# Set up the thread to do asynchronous I/O
# More can be made if necessary
self.running = 1
self.thread1 = threading.Thread(target=self.workerThread1)
self.thread1.start()
#Start receiving thread.
self.rx = threading.Thread(target=self.rx)
self.rx.start()
# Start the periodic call in the GUI to check if the queue contains
# anything
self.periodicCall()
def periodicCall(self):
"""
Check every 100 ms if there is something new in the queue.
"""
self.gui.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
self.master.after(100, self.periodicCall)
def workerThread1(self):
"""
This is where we handle the asynchronous I/O. For example, it may be
a 'select()'.
One important thing to remember is that the thread has to yield
control.
"""
while self.running:
# To simulate asynchronous I/O, we create a random number at
# random intervals. Replace the following 2 lines with the real
# thing.
time.sleep(rand.random() * 0.3)
msg = rand.random()
#self.queue.put(msg)
# Continuously read and print packets
def rx(self):
global x
self.ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1, bytesize=8, stopbits=1)
self.ser.flush()
while(1):
response = str(self.ser.readline())
# Send movement codes.
if x == "'a'" or x == "'A'": # or "'A'": # Turn left.
self.ser.write('4')
elif x == "'w'" or x == "'W'": #Go forward.
self.ser.write("3")
elif x == "'d'" or x == "'D'": #Turn right.
self.ser.write("2")
elif x == "'s'" or x == "'S'": #Go back.
self.ser.write("1")
elif x == "'x'" or x == "'X'": #Stop.
self.ser.write("5")
elif x == "'1'": #Raise speed.
self.ser.write("7")
elif x == "'2'": #Lower speed.
self.ser.write("6")
x = ""
if len(response) > 10:
self.ser.flushInput() #If you don't flush the buffer, then it'll try to read out all
#the sensor readings, so the reaction time will be extrordinary.
time.sleep(.1)# This scales back the CPU usage.
self.queue.put(response)
def endApplication(self):
print "Closing"
self.running = 0
self.ser.close()
sys.exit(1)
rand = random.Random()
root = Tkinter.Tk()
client = ThreadedClient(root)
root.mainloop()
Your self.gui is GuiPart. GuiPart doesn't have processIncoming.
In your code snippet displayed above the lines
def key(self, event):
and
def processIncoming(self):
are no longer indented by four blank spaces as they probably should to become methods of your class GuiPart.
Python newcomers coming from other languages often miss the detail that indentation is a very important part of the syntax in the Python programming language. Therefore it is also very essential to avoid mixing TABs and spaces.
(Especially when copying code snippets from various sources which is otherwise
a great opportunity to learn and experiment with new things)
Another thing is: In Python you can also define module level global functions.
The syntax to do this is practically the same as the syntax used to define a method within a class (with the exception of the indentation level).
So in your example code snippet the former methods key() and processIncoming() had become module global function definitions instead of methods simply by wrong indentation.
A method is an attribute of a class object. These two methods were moved
from the class name space one level up to module name space.
Hence the following error message:
AttributeError: GuiPart instance has no attribute 'processIncoming'