7

I would like to run a command line tool to run in a separate function and passed to the button click the additional command for this program but each time I get this as a response.

takes 1 positional argument but 2 were given

from tkinter import *
import subprocess


class StdoutRedirector(object):
    def __init__(self,text_widget):
        self.text_space = text_widget

    def write(self,string):
        self.text_space.insert('end', string)
        self.text_space.see('end')

class CoreGUI(object):
    def __init__(self,parent):
        self.parent = parent
        self.InitUI()

        button = Button(self.parent, text="Check Device", command= self.adb("devices"))
        button.grid(column=0, row=0, columnspan=1)

    def InitUI(self):
        self.text_box = Text(self.parent, wrap='word', height = 6, width=50)
        self.text_box.grid(column=0, row=10, columnspan = 2, sticky='NSWE', padx=5, pady=5)
        sys.stdout = StdoutRedirector(self.text_box)

    def adb(self, **args):
        process = subprocess.Popen(['adb.exe', args], stdout=subprocess.PIPE, shell=True)
        print(process.communicate())
        #return x.communicate(stdout)


root = Tk()
gui = CoreGUI(root)
root.mainloop()

the error

Traceback (most recent call last):
  File "C:/Users/Maik/PycharmProjects/Lernen/subprocessExtra.py", line 33, in <module>
    gui = CoreGUI(root)
  File "C:/Users/Maik/PycharmProjects/Lernen/subprocessExtra.py", line 18, in __init__
    button = Button(self.parent, text="Check Device", command= self.adb("devices"))
TypeError: adb() takes 1 positional argument but 2 were given
Exception ignored in: <__main__.StdoutRedirector object at 0x013531B0>
AttributeError: 'StdoutRedirector' object has no attribute 'flush'

Process finished with exit code 1

can some body help me

there is something wrong with **args

Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
MrChaosBude
  • 83
  • 1
  • 1
  • 6
  • 1
    Can we see the exact error, and the specific line where it happens – Andrew Li Jun 13 '16 at 14:04
  • ´Traceback (most recent call last): File "*/subprocessExtra.py", line 33, in gui = CoreGUI(root) File "*/subprocessExtra.py", line 18, in __init__ button = Button(self.parent, text="Check Device", command= self.adb("devices")) TypeError: adb() takes 1 positional argument but 2 were given Exception ignored in: <__main__.StdoutRedirector object at 0x013531B0> AttributeError: 'StdoutRedirector' object has no attribute 'flush' Process finished with exit code 1 ´ – MrChaosBude Jun 13 '16 at 14:10
  • 2
    That should be in the question :^) – Andrew Li Jun 13 '16 at 14:10
  • It looks like the code using the redirector is expecting a `flush` method, which you don't provide. Have you tried adding a `flush` method to the redirector? You are also using the `command` attribute wrong. See http://stackoverflow.com/q/5767228/7432 – Bryan Oakley Jun 13 '16 at 14:17
  • I try to understand through code snippets and try as they learn how to code. lambda dont worke – MrChaosBude Jun 13 '16 at 14:24

2 Answers2

4

It is because you are providing it a positional argument here:

button = Button(self.parent, text="Check Device", command= self.adb("devices"))

command want's a callback function. and you are passing it the response from the adb method. (see here fore more: http://effbot.org/tkinterbook/button.htm)

when that line is being called, self.adb("devices") is being called. if you look at your definition of adb

def adb(self, **args):

You are only asking for 1 positional argument self and any number of keyword arguments **args then you are calling it self.adb("devices") with 2 positional arguments of self and "devices"

What you will need to do is have an intermediate method, if you want to have the adb method more general, or just put "devices" into the adb method.

edit

See also here: http://effbot.org/zone/tkinter-callbacks.htm See the section "Passing Argument to Callbacks"

edit 2: code example

If you do this, it should work:

button = Button(self.parent, text="Check Device", command=lambda:  self.adb("devices"))

and then change your function to a single * inlieu of a ** (keyword arg expansion) See here: https://stackoverflow.com/a/36908/6030424 for more explanation.

def adb(self, *args):
    process = subprocess.Popen(['adb.exe', args], stdout=subprocess.PIPE, shell=True)
    print(process.communicate())
    #return x.communicate(stdout)
Community
  • 1
  • 1
Tom Myddeltyn
  • 1,307
  • 1
  • 13
  • 27
  • I would like to use devices not fixed because I want to use the same definition to create other and more agumente yet – MrChaosBude Jun 13 '16 at 14:43
  • if i add `button = Button(self.parent, text="Check Device", command=lambda: self.adb(self, "devises"))` error say `button = Button(self.parent, text="Check Device", command=lambda: self.adb(self, "devises")) TypeError: adb() takes 2 positional arguments but 3 were given Exception ignored in: <__main__.StdoutRedirector object at 0x01193230> AttributeError: 'StdoutRedirector' object has no attribute 'flush'` – MrChaosBude Jun 13 '16 at 14:54
4

The problem is in the way you declare args: it should be *args (one asterisk) instead of **args (two asterisks). One asterisk specifies any number of positional arguments, where as two asterisks means any number of named arguments.

Also, you need to pass args correct to adb.exe:

def adb(self, *args):
    process = subprocess.Popen(['adb.exe'] + args, stdout=subprocess.PIPE, shell=True)
Hai Vu
  • 37,849
  • 11
  • 66
  • 93