How to correctly create a second window in pyqt4 - python-2.7

I'm trying to open a second window beside my mainwindow on a button click and display a tablewidget on it with some data. When I open the window it raises
AttributeError: 'Ui_MainWindow' object has no attribute 'openTable'
The mainwindow was created with Qt Designer and converted the ui file with pyuic4. How do I do this correctly correct so that the error is not raised?
The button calls function:
def showCliplist(self):
data = self.metadata_list
luts = self.lutlist
selected_lut = self.LUTBox.currentIndex()
openTable = ClipListViewer(data,luts,selected_lut)
self.openTable.show()
New window class:
class ClipListViewer(QtGui.QWidget):
def __init__(self, data, luts, selected_lut, parent = None):
super(ClipListViewer,self).__init__()
self.setWindowTitle('Cliplist')
self.resize(900,600)
self.metadata = data
self.curentluts = luts
self.choosenlut = selected_lut
y_count = len(self.metadata)
self.table = QtGui.QTableWidget(y_count,6)
self.table.setHorizontalHeaderLabels(['Clip', 'Videocodec', 'FPS', 'Audiocodec', 'Start Timecode', 'LUT'])
x = y = 0
for items in self.metadata:
for entry in items:
#print entry
self.table.setItem(y, x, QtGui.QTableWidgetItem(entry))
self.table.resizeColumnToContents(x)
x += 1
self.comb = QtGui.QComboBox()
for lutname in self.curentluts:
self.comb.addItem(lutname)
self.comb.setCurrentIndex(self.choosenlut)
self.table.setCellWidget(y, 5, self.comb)
self.table.setColumnWidth(5, 230)
y += 1
x = 0
self.table.resizeRowsToContents()
layout = QtGui.QHBoxLayout()
layout.addWidget(self.table)
self.setLayout(layout)
self.show()
def closeEvent(self, event): #check if window was closed
print "Cliplist Window was closed! "

I reviews your code completed, OK, let's me explain.
AttributeError: 'Ui_MainWindow' object has no attribute 'openTable'
This error says, "I not have self.openTable in class Ui_MainWindow (That true because your have create own method)".
Why ? : Because a problem is in line this;
def showCliplist(self):
data = self.metadata_list
luts = self.lutlist
selected_lut = self.LUTBox.currentIndex()
openTable = ClipListViewer(data,luts,selected_lut) # <- (1) THIS LINE
self.openTable.show() # <- (2) THIS LINE
In (1), your create your second widget in to openTable (Not self.openTable).
This line we have this object in openTable (Not self.openTable).
Then (2), your call QtGui.QWidget.show(self) to show widget of self.openTable (Not openTable). It's should be error because we don't have variable self.openTable. To fix it your should use same name variable, Like this;
def showCliplist(self):
data = self.metadata_list
luts = self.lutlist
selected_lut = self.LUTBox.currentIndex()
self.openTable = ClipListViewer(data,luts,selected_lut) # <- FIX THIS LINE !
self.openTable.show() # <- (2) THIS LINE
Why second widget is show before I call self.openTable.show()?
Answer : Your can see in second widget initiate in last line of code your see QtGui.QWidget.show(self) has be call before end of initiate;
class ClipListViewer(QtGui.QWidget):
def __init__(self, data, luts, selected_lut, parent = None):
super(ClipListViewer,self).__init__()
.
.
.
self.show() # <- THIS LINE
Regards,

Related

Tkinter assign buttons to entries in dynamically created widgets

How can I access an Entry content with pressing the corresponding Button in dynamically created widgets?
Below is the best I come up with so far. Thank you for any help.
from Tkinter import *
class App(object):
def __init__(self, master):
self.master = master
self.mf = Frame(self.master)
self.l = ["white", "red", "blue", "brown"]
self.font = ("Arial", 30)
self.c, self.r = 1, 0
self.cc, self.rr = 0, 0
self.bel = []
for c in self.l:
action = self.print_entry
self.e = Entry(self.mf, bg=c, width=10, font=self.font)
self.e.grid(row=self.r, column=self.c)
self.b = Button(self.mf, bg=c, text=c, font=self.font)
self.b.grid(row=self.rr, column=self.cc)
self.b.config(command=action)
self.bel.append((self.b, self.e))
self.rr += 1
self.r += 1
self.mf.pack()
def print_entry(self): # this function prints the content of the entry
pass
def main():
root = Tk()
display = App(root)
root.mainloop()
if __name__=="__main__":
main()
You can pass a reference to the entry widget into the command, using lambda or functools.partial. For example:
self.b.config(command= lambda entry=self.e: action(entry))
...
def print_entry(self, entry):
print("the entry is '%s'" % entry.get())
By the way, using self.b and self.e is pointless, since those variables will only ever hold references to the last button and last entry. You should either use a local variable, and/or append the values to a list.

Cannot populate radio button correctly - Tkinter

I am trying out few things with Tkinter as an exercise.
In my example app i want user to select one option from drop down list. Based on this selection i want to populate the list of radio button. I have created a list of values to populate in dropdown list and a dictionary for radio button. please check the code.
See below some working and not working examples:
As you can see from the picture. The first window works well. Second also. However the third window is not quite correct. The option '410' remains. I am making subsequent selections without closing the application.
I think perhaps i am not declaring the variables at the right place. It would be very helpful if some one can have a look at the code and rectify it.
Code:
from Tkinter import *
import ttk
class App(Frame):
def __init__(self,parent):
Frame.__init__(self)
self.parent = parent
self.v = IntVar()
#self.radio_value = []
#self.i = 0
self.GUI()
def GUI(self):
self.master.title('Example')
self.pack(fill = BOTH, expand = 1)
self.options = ['a1','a2','a3','a4','a5']
self.box_value = StringVar()
self.box = ttk.Combobox( self, textvariable=self.box_value)
self.box.bind("<<ComboboxSelected>>", self.set_Radio)
self.box['values'] = self.options
self.box.current(0)
self.box.grid(row = 0, column = 0 )
self.choices = {'a1':['30', '70', '140', '410'], 'a2': ['a', 'b', 'c'], 'a3': ['x', 'y', 'z'], 'a4':['p', 'q', 'r'], 'a5': ['l', 'm', 'n']}
def set_Radio(self,parent):
i = 0
radio_value = []
if self.box_value.get() in self.choices.keys():
radio_value = self.choices[self.box_value.get()]
print radio_value
for t in radio_value:
i = i+1
b = Radiobutton(self, text=t, variable=self.v, value=t)
b.grid(row = i, column = 0)
def main():
root = Tk()
root.geometry('250x250')
app1= App(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem is that you don't delete the old radiobuttons before creating the new radiobuttons. One solution is to put them in an invisible frame. When you delete the frame, the radiobuttons will automatically be destroyed. Another solution is to keep a reference to them so that you can destroy them individually later.
Here's an example of keeping a reference:
def __init-_(self, parent):
...
self.radios = []
def set_Radio(self,parent):
for widget in self.radios:
widget.destroy()
self.radios = []
...
for t in radio_value:
...
b = Radiobutton(...)
self.radios.append(b)
...

Using Python Tkinter .config() method

I am trying to use the Python Tkinter .config() method to update some message text. I can't get it to work. What might I be doing wrong (see the update_message method):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
import threading
import Queue
# styles
BACKROUND_COLOR = '#000000'
TYPEFACE = 'Unit-Bold'
FONT_SIZE = 50
TEXT_COLOR = '#777777'
TEXTBOX_WIDTH = 400
# text
TITLE = 'listen closely'
SCORE_MESSAGE = 'your score:\n '
END_MESSAGE = 'too loud!\ntry again'
# configuration
DEVICE = 'hw:1' # hardware sound card index
CHANNELS = 1
SAMPLE_RATE = 8000 # Hz // 44100
PERIOD = 256 # Frames // 256
FORMAT = aa.PCM_FORMAT_S8 # Sound format
NOISE_THRESHOLD = 3
class Display(object):
def __init__(self, parent, queue):
self.parent = parent
self.queue = queue
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(
parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title(TITLE)
parent.configure(background=BACKROUND_COLOR)
parent.displayFont = tkFont.Font(family=TYPEFACE, size=FONT_SIZE)
self.process_queue()
def process_queue(self):
try:
score = self.queue.get(0)
self.print_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def update_queue(self):
try:
score = self.queue.get(0)
self.update_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def print_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message = tk.Message(
self.parent, text=displayString, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR, width=TEXTBOX_WIDTH, justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def update_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message.config(text=displayString)
def setup_audio(queue, stop_event):
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while not stop_event.is_set():
# Read data from device
l, data = data_in.read()
if l:
# catch frame error
try:
max_vol = audioop.rms(data, 2)
scaled_vol = max_vol // 4680
print scaled_vol
if scaled_vol <= 3:
# Too quiet, ignore
continue
queue.put(scaled_vol)
except audioop.error, e:
if e.message != "not a whole number of frames":
raise e
def main():
root = tk.Tk()
queue = Queue.Queue()
window = Display(root, queue)
stop_event = threading.Event()
audio_thread = threading.Thread(target=setup_audio,
args=[queue, stop_event])
audio_thread.start()
try:
root.mainloop()
finally:
stop_event.set()
audio_thread.join()
pass
if __name__ == '__main__':
main()
I don't want to be laying down a new message every time I update. If the .config() doesn't work, is there another method to update the text configuration of the message?
I would use string variables, first create your string variable then set it to want you want it to display at the start next make your object and in text put the sting variable then when you want to change the text in the object change the string variable.
self.messaget = StringVar()
self.messaget.set("")
self.message = tk.Message(
self.parent, textvariable=self.messaget, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR,
width=TEXTBOX_WIDTH, justify="c").grid()
#note renember to palce the object after you have created it either using
#.grid(row = , column =) or .pack()
#note that it is textvariable instead of text if you put text instead it will run but
#but will show PY_Var instead of the value of the variable
edit
to change the text without recreating the object you do the name of the string variable you have used and .set
self.messaget.set("hi")

Tkinter button doesn't respond (has no mouse over effect)

I'm writing a game that has info that is communicated from client to server and from server to client. One specific (non-playing) client is the monitor, which only displays the game board and players. This works fine, the only thing that doesn't work is the quit button on the GUI. A minor thing, but I would like it to work. :) Plus I think that there might be something pretty wrong with the code, even though it works.
I tried all kind of different commands (sys.exit, quit...) and nothing fixed it.
There's no error message, nothing happens with the button at all. No mouse over effect, nothing if I click it. Relevant code (I removed matrix and server logic because I think it's irrelevant - if it isn't I'll post it):
class Main():
def __init__(self, master):
self.frame = Frame(master)
self.frame.pack()
# Has to be counted up by server class
rounds = 0
# Has to be communicated by server class. If numberwin == numberrobots,
# game is won
numberwin = 0
numberrobots = 2
def draw(self):
if hasattr(self, 'info'):
self.info.destroy()
if hasattr(self, 'quit'):
self.quit.destroy()
print "Main should draw this matrix %s" % self.matrix
[...] lots of matrix stuff [...]
# Pop-Up if game was won
# TODO: Make GUI quittable
if self.numberwin == self.numberrobots:
self.top = Toplevel()
self.msg = Message(self.top, text="This game was won!")
self.msg.pack(side=LEFT)
self.quittop = Button(
self.top, text="Yay", command=self.frame.destroy)
self.quittop.pack(side=BOTTOM)
# TODO: Quit GUI
self.quit = Button(self.frame, text="Quit", command=self.frame.destroy)
self.quit.pack(side=BOTTOM)
# Information on the game
self.info = Label(
self.frame, text="Rounds played: {}, Numbers of robots in win condition: {}".format(self.rounds, self.numberwin))
self.info.pack(side=TOP)
def canvasCreator(self, numberrows, numbercolumns):
# Game board
self.canvas = Canvas(
self.frame, width=numbercolumns * 100 + 10, height=numberrows * 100 + 10)
self.canvas.pack()
class Agent(Protocol, basic.LineReceiver):
master = Tk()
main = Main(master)
# So first matrix is treated differently from later matrixes
flagFirstMatrix = 1
def connectionMade(self):
msg = dumps({"type": "monitor"})
self.sendLine(msg)
print "Sent message:", msg
def dataReceived(self, data):
# Decode the json dump
print "Data received: %s" % data
data = loads(data)
self.main.matrix = np.matrix(data["positions"])
self.main.goals = np.matrix(data["goals"])
self.main.next_move_by_agent = data["next_move"]
self.main.rounds = data["rounds"]
self.main.numberwin = data["win_states"]
if self.flagFirstMatrix == 1:
self.main.numberrows, self.main.numbercolumns = self.main.matrix.shape
self.main.canvasCreator(
self.main.numberrows, self.main.numbercolumns)
self.main.canvas.pack()
self.flagFirstMatrix = 0
self.main.canvas.delete(ALL)
self.main.draw()
self.master.update_idletasks()
self.master.update()
First there is no indentation for class Agent, second for the quit button's "call back" self.frame.destroy is never defined so it doesn't do anything. If you meant tkinter destroy method try self.frame.destroy() or try explicitly defining it. You can also try calling either fram.pack_forget() or fram.grid_forget()
Add master.mainloop() to your last line in terms of the entire lines of code

combobox value is not selecting in python when it is created dynamically

I created a Tkinter window Form, in that based on user selection i dynamically replace the combobox with deleting the Exist one.But the Problem is when i do like this the value selected in combobox is not updated.It is always displaying the default vale. see the following code
from Tkinter import *
import ttk
final=[]
field_0=['1','0']
field_1=['1','2','23','45','6']
field_2=['2','5','7','8','9']
class header:
def __init__(self,root):
self.parent=root
self.row_number= 1
self.value=0
root.title(" Select fields ")
root.minsize(width=450, height=530)
root.maxsize(width=450, height=550)
frame=Frame(root,height=20,relief=FLAT)
frame.grid(row=0,column=0,sticky=W)
Label(frame,text="Enter value:").grid(row=0,column=0,padx=70,pady=5,sticky=W)
entryValue=StringVar()
port_e = Entry(root,width=5,textvariable=entryValue)
port_e.delete(0,END)
port_e.insert(0,'0')
port_e.grid(row=0,column=0,padx=100,pady=5)
self.value=self.value+1
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
self.combo.bind("<<ComboboxSelected>>",self.selected_field)
final.append(self.combo)
def selected_field(self,event):
global cnt_sel
print final[0].get()
if(final[0].get()=='1'):
self.control=Label(root,text="Choose one type").grid(row=self.change_row,column=0,padx=20,pady=5,sticky=W)
self.combocreate(self.change_row,root,field_1)
final[1]=final[2] #replacing combobox dynamically based on selection
del final[2]
elif(final[0].get()=='0'):
Label(root,text="Choose zerotype ").grid(row=self.change_row,column=0,padx=20,pady=5,sticky=W)
self.combocreate(self.change_row,root,field_2)
final[1]=final[2]
del final[2]
else:
pass
if __name__=="__main__":
root=Tk()
app=header(root)
root.mainloop()
The problem with your code is that every time combocreate is called, it automatically binds the Combobox to self.selected_field, so whenever you change the value of the type Combobox, you call self.selected_field, and it automatically resets the type to the default. To fix the problem, you should only bind the first Combobox. This should fix the problem:
Replace
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
With
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
final[0].bind("<<ComboboxSelected>>",self.selected_field)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
And
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
self.combo.bind("<<ComboboxSelected>>",self.selected_field)
final.append(self.combo)
With
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
final.append(self.combo)