Python TkInter bind breaking - python

I have a simple GUI which uses key binds - something like this.
import Tkinter, tkFileDialog
class Foo(object):
def __init__(self, master):
master.bind('3', self.do_bar)
master.bind('9', self.load_new_config)
self.load_config()
if not self.conf:
self.load_new_config()
else:
self.load_data()
def load_config(self):
try:
self.conf = #get stuff from known file
except FailedToGetStuff:
self.conf = None
def load_new_config(self):
path = askopenfilename(initialdir='~')
self.conf = #get stuff from file in path
self.load_data()
def load_data(self):
#get data from self.conf, process and display
def do_bar(self):
#do stuff with displayed data
if __name__ == "__main__"
root = Tk()
Foo(root)
root.mainloop()
Now, this works just fine when load_config() finds what it was looking for. I can use the binds and even after using '9' and loading new config, everything works.
Problem is, if load_config() fails, self.conf gets set to None and load_new_conf gets called from __init__, the binds are no longer operational.
I figured out that the problem is caused by tkFileDialog.askopenfilename() being called from within __init__. What I don't understand is why this happens and how to get around it.

This code works for me:
import Tkinter, tkFileDialog
class Foo(object):
def __init__(self, master):
master.bind('<KeyPress-3>', self.do_bar)
master.bind('<KeyPress-9>', self.load_new_config)
self.load_config()
if not self.conf:
master.after(1, self.load_new_config)
else:
self.load_data()
def load_config(self):
try:
self.conf = None#get stuff from known file
except FailedToGetStuff:
self.conf = None
def load_new_config(self, e = 0):
path = tkFileDialog.askopenfilename(initialdir='~')
self.conf = None#get stuff from file in path
self.load_data()
def load_data(self, e = 0):
pass
#get data from self.conf, process and display
def do_bar(self, e = 0):
print 1
#do stuff with displayed data
if __name__ == "__main__":
root = Tkinter.Tk()
Foo(root)
root.mainloop()

Related

Passing user entry to another script

I am trying to use a variable that I get from an entry field in tkinter to another script.
In short:
I want to use the user's input in an entry field in another script. This does not work at all.
Any help highly appreciated!
I tried so far for Script2:
from Script1 import App
test = App()
print(test.write_slogan(self))
TypeError: init() missing 1 required positional argument: 'master'
and
from Script1 import App
print(App.write_slogan())
write_slogan() missing 1 required positional argument: 'self'
and
from Script1 import App
print(App.write_slogan(self))
NameError: name 'self' is not defined
and
import Script1
print(Script1.App.a)
AttributeError: type object 'App' has no attribute 'a'
Script1:
from tkinter import *
class App:
a = 0
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.slogan = Button(frame,
text="Hello",
command=self.write_slogan)
self.slogan.pack(side=LEFT)
self.entry1 = Entry(root, width=15)
self.entry1.pack(side=LEFT)
self.importbutton = Button(frame,
text="import",
command=self.importing)
self.importbutton.pack(side=LEFT)
def write_slogan(self):
print ("Test!")
App.a = self.entry1.get()
print(App.a)
return App.a
def importing(self):
print('Import')
import Script2
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
Script2:
import Script1
You've since fixed this I believe, but I'm remarking the information for others that may find their way here: The self within a class function is the instance python will automatically pass when calling the defs. (Unless you're using classmethod or staticmethod for which are not useful in your use case and are a slightly more advanced topic)
# -- Script1 \/
class App:
def __init__(self, master):
# self here is automatically passed
# but you need to pass the "master" arg.
frame = Frame(master)
# ... Example:
self._value = 0
def set_value(self, val):
self._value = val
def get_value(self):
return self._value
# -- Use (probably in Scipt2)
master = Tk()
app = App(master)
print (app.get_value()) # Notice how we don't pass self
>>> 0
app.set_value("my new value") # This string is the "val" arg
print (app.get_value())
>>> my new value
Scipt1.App.a issue
The main issue you're having with is most likely to do with the way python manages modules. The class is writing to App.a in Script1.App but not Script2.Script1.App.a. This is expected behavior so I recommend instead trying to work with something like:
class App:
def __init__(self, master):
# ... Make your tk widgets as you already have
def set_entry_value(self, val):
self.entry1.set(val)
def get_entry_value(self):
self._last_recieved_entry_value = self.entry1.get()
# -- Script2 \/
# The if __name__ == '__main__' is not run on imported
# scripts, only the script python starts execution on
# ~$> python Script2.py
import Script1
if __name__ == '__main__':
root = Tk()
app = Script1.App(root)
# Possibly set a default?
app.set_entry_value("Default value")
root.mainloop()
# - Some time after the mainloop is over
my_lastest_value = root.get_entry_value()
This way, you're letting the local instance of objects handle their internal values. If you're looking to set a class member of an alternate module, then doing so in Script2 may work.
if __name__ == '__main__':
root = Tk()
app = Script1.App(root)
# Do something to set the entry
Script1.App.a = app.get_entry_value()
But be warned, that may not scale across multiple modules.

Module-wide variables python

I am trying to import a value from my module called SignIn and use it in a main module to apply a sqlite query to it. The variable is called username and is assigned when the user enters it in a tkinter window. I also have a database passwordDbase in the python file Password_database that has all of the usernames in it. How do I import this value into another module without circular dependency/cycle importing ('as i keep getting SignIn is not callable'). Thanks in advance.
SignIn looks like:
import tkinter
def login():
global username
username=entry_user.get()
win=tkinter.Tk()
entry_user=tkinter.Entry(win)
#with a bunch of other tkinter functions to make textboxes etc
My main programme looks like:
import sqlite3
import SignIn
import Password_database
SignIn()
Password_database()
conn = sqlite3.connect ('passwordDbase.db')
c = conn.cursor()
username=SignIn.username
username=username.strip()
c.execute('''SELECT * FROM passwordDbase WHERE employee_username=?''',(username,))
rows = c.fetchall()
row = c.fetchone()
rows=c.fetchall()
if len(rows) != 1:
message=' '
else:
message= 'correct'
conn.commit()
c.close()
conn.close()
Maybe it doesn't resolve your problem but I would organize all in different way.
Inside mydatabase.py I would keep functions which work with database
import sqlite3
def get_user(username):
conn = sqlite3.connect('passwordDbase.db')
c = conn.cursor()
c.execute('SELECT * FROM passwordDbase WHERE employee_username=?', (username,))
conn.commit()
rows = c.fetchall()
if len(rows) != 1:
message = 'error'
data = None
else:
message = 'correct'
data = rows[0]
c.close()
conn.close()
return message, data
def check_pasword(username, password):
pass
def other_function_on_database():
pass
And I would import this into code with tkinter
import tkinter as tk
import mydatabase
# --- functions ---
def login():
username = entry_username.get()
password = entry_password.get()
username = username.strip()
message, data = mydatabase.login(username)
result_label['text'] = message
#if data and data[x] == password:
# result_label['text'] = "correct"
# --- main ---
win = tk.Tk()
entry_username = tk.Entry(win)
entry_username.pack()
entry_password = tk.Entry(win)
entry_password.pack()
button_login = tk.Button(win, text="Login", command=login)
button_login.pack()
result_label = tk.Label(win, text="")
result_label.pack()
win.mainloop()
This way file with tkinter is main file and I build GUI wrapper for database functions.
First of all, to prevent code execution at import, use
#mymodule.py
class MyModule(object):
def __init__(self, *args, **kwargs):
pass
def MyFunc(myparam):
print myparam
if __name__ == "__main__":
# this code gets executed when the module is called like
# python mymodule.py
print "MyModule called, not imported"
If you want it executed at import, place it inside the module-scope, not the "if __name__ == "__main__":" scope.
Second, if you want to import "static" values from a module define them inside the module like:
#mymodule.py
MyModuleText="Text"
MyModuleList=["My", "Module", "List"]
class MyModule(object):
def __init__(self, *args, **kwargs):
pass
def MyFunc(myparam):
print myparam
if __name__ == "__main__":
# this code gets executed when the module is called like
# python mymodule.py
print "MyModule called, not imported"
then use
#!/usr/bin/python
import mymodule
print mymodule.MyModuleText
print mymodule.MyModuleList
this will access the variables from the module.
If you want to run functions from the module, call them.
To use classes, create instances.
#mymodule.py
MyModuleText="Text"
MyModuleList=["My", "Module", "List"]
class MyModule(object):
def __init__(self, *args, **kwargs):
pass
def some_method(self, text):
print "MyModule.some_func %s"%text
def MyFunc(myparam):
print myparam
if __name__ == "__main__":
# this code gets executed when the module is called like
# python mymodule.py
print "MyModule called, not imported"
#!/usr/bin/python
import mymodule
myinstance = mymodule.MyClass()
myinstance.some_method("SomeMethod from outside")
mymodule.MyFunc("MyModuleFunction called")

Python main function in class

I'm new to python and the main() method and class def's are confusing me. I'm trying to create a bloom filter and my program keeps terminating because I don't think I'm calling things correctly.
class BloomFilter(object):
def __init__(self, numBits, numHashFunctions):
self.numBits = numBits
self.bitArray = [0] * numBits
self.hash = bloomFilterHash(numBits, numHashFunctions)
def insert(self, key):
def lookup(self, key):
def rand_inserts(self,num):
def main(): #not sure if i should put this inside or outside class
bloomfilter = BloomFilter(100,5)
bloomfilter.rand_inserts(15)
if __name__ == '__main__':
BloomFilter().main()
So if I wanted to create a bloom filter with 100 numBits and 5 hash functions, should i call that under the if __name__ == '__main__' or under def main()? I'm not sure if I'm calling these correctly as I'm much more familiar with Java. thanks!
def main():
bloomfilter = BloomFilter(100,5)
bloomfilter.rand_inserts(15)
the name == '__main__' clause is to make sure your code only runs when the module is called directly, not, for instance, if you are importing something from the module in another module. main() is not a special method for a python class, so I believe your objective here, in a simplified way, is the following:
class BloomFilter(object):
def __init__(self, numBits, numHashFunctions):
self.numBits = numBits
self.bitArray = [0] * numBits
self.hash = bloomFilterHash(numBits, numHashFunctions)
if __name__ == '__main__':
# creates an instance of the class
bloomfilter = BloomFilter(100,5)
# apply some method to instance...
bloomfilter.rand_inserts(15)
You would want to put main() outside the class:
class BloomFilter(object):
def __init__(self, numBits, numHashFunctions):
self.numBits = numBits
self.bitArray = [0] * numBits
self.hash = bloomFilterHash(numBits, numHashFunctions)
def insert(self, key):
def lookup(self, key):
def rand_inserts(self,num):
def main():
some_value = Bloomfilter(100, 5)
some_value.rand_inserts(15)
main()

Python return value from class when an event happen

I have the main application in file main.py and some UI classes in uiwidgets.py.
in main.py I have:
import uiwidgets as uiw
uiw.MultiColumnListbox(header, data)
def doSomething(self, n)
do something with n
in uiwidgets.py I have:
class MultiColumnListbox(object):
def __init__(self, header, data):
self.header=header
self.data=data
...
self.tree.bind( "<Double-Button-1>", self.OnClick)
def OnClick(self, event):
global return_index
item = self.tree.identify('item',event.x,event.y)
if item is not "":
return_index = (int((item[1:4]),16) - 1)
n = self.data[return_index][0]
I need to return the n value from class to main.py when the user click the widget. How can I do?
You could just create a global variable in uiwidgets.py at the and and name it 'transfervar' or something like that. Then in main.py you import uiwidgets.py again. It should give you accses to 'transfervar' in main.py.
If the value of n is complicated or long, you can also write it into a textfile. But then you need the know-how how to write and read files. This is very nice to learn in "Think pythonger", by Allen B. Downey, chapter 14.
Your code with the global variable looks like this:
transfervar = None #just to create, you could set it 0, too
class MultiColumnListbox(object):
def __init__(self, header, data):
self.header=header
self.data=data
...
self.tree.bind( "<Double-Button-1>", self.OnClick)
def OnClick(self, event):
global return_index
item = self.tree.identify('item',event.x,event.y)
if item is not "":
return_index = (int((item[1:4]),16) - 1)
n = self.data[return_index][0]
global transfervar #needs to be declared as global
transfervar = n

PyQt Custom Data Structure

I have a simple PyQt4 program that takes in 3 Particulars (Name,Gender & Address) whenever i clicked on OK button and save it as a binary file (3 particulars are hard coded in program for testing purpose). Then later will load that information back and display it in QTableWidget.
This is the layout of my program:
It has 2 scripts: DContainer.py and Data_Main.py
Dcontainer.py
import bisect
from PyQt4 import QtCore
class Person(object):
def __init__(self, Name = None, Gender = None , Address = None ):
self.Name = Name
self.Gender = Gender
self.Address = Address
class PersonContainer(object):
def __init__(self):
self.__fname = QtCore.QString("mydatabase.mqb")
self.__persons = []
self.__personFromId = {}
def __iter__(self):
for pair in iter(self.__persons):
yield pair[1]
def __len__(self):
return len(self.__persons)
def Clear(self):
self.__persons = []
self.__personFromId ={}
def add(self,person):
if id(person)in self.__personFromId:
return False
key = person.Name
bisect.insort_left(self.__persons, [key,person])
self.__personFromId[id(person)] = person
return True
def save(self):
fh = QtCore.QFile(self.__fname)
if not fh.open(QtCore.QIODevice.WriteOnly):
raise IOError , unicode(fh.errorString())
stream = QtCore.QDataStream(fh)
for key, person in self.__persons:
stream << person.Name << person.Gender << person.Address
def load(self):
fh = QtCore.QFile(self.__fname)
if not fh.open(QtCore.QIODevice.ReadOnly):
raise IOError , unicode(fh.errorString())
stream = QtCore.QDataStream(fh)
while not stream.atEnd():
Name = QtCore.QString()
Gender = QtCore.QString()
Address = QtCore.QString()
stream >> Name >> Gender >> Address
self.add(Person(Name,Gender,Address))
Data_Main.py
import sys
from PyQt4 import QtCore,QtGui
import DContainer
class MainDialog(QtGui.QDialog):
def __init__(self, parent = None):
super(MainDialog,self).__init__(parent)
self.InitGui()
self.persons = DContainer.PersonContainer()
self.Update()
def InitGui(self):
buttonbox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
self.table = QtGui.QTableWidget()
layout = QtGui.QVBoxLayout()
layout.addWidget(self.table)
layout.addWidget(buttonbox)
self.setLayout(layout)
self.connect(buttonbox.button(buttonbox.Ok), QtCore.SIGNAL("clicked()"),self.OK)
def OK(self):
NewPerson = DContainer.Person(QtCore.QString('This is another test'),QtCore.QString('Male'),QtCore.QString('Strand Road'))
self.persons.add(NewPerson)
self.persons.save()
self.Update()
def Update(self):
self.table.clear()
self.persons.load()
self.table.setRowCount(len(self.persons))
self.table.setColumnCount(3)
for row,person in enumerate(self.persons):
item = QtGui.QTableWidgetItem(person.Name)
self.table.setItem(row,0,item)
def Main():
app = QtGui.QApplication(sys.argv)
dialog = MainDialog()
dialog.show()
app.exec_()
if __name__ == "__main__":
Main()
My Problem is whenever i clicked on OK button, it create multiple table entries
After second click
It should not create multiple table entries as i have used
if id(person)in self.__personFromId:
return False
in my Add method in Dcontainer.py.
Rightfully, it should only show one item in the table unless i give the new person object with different name.
What is causing the problem?
The PersonContainer.add method is called twice when you click the OK button:
Directly from the MainDialog.OK method
Indirectly from the MainDialog.Update method, with self.persons.load()
You can add an optional argument to the Update method to trigger the call to load:
def Update(self, load=False):
self.table.clear()
if load:
self.persons.load()
And call this method with load set to True in the __init__ method:
def __init__(self, parent = None):
super(MainDialog,self).__init__(parent)
self.InitGui()
self.persons = DContainer.PersonContainer()
self.Update(True)
By the way, the old style signal/slot is no longer supported with PyQt5. This is how to write in the new style:
buttonbox.accepted.connect(self.OK)
buttonbox.rejected.connect(self.reject)

Categories

Resources