I am working on a project that involves creating many instances of Tkinter Labels and Entry widgets that will always be aligned next to one another. To try and save myself time, I created a custom class that I am showing below:
class labelEntry(Label,Entry):
def __init__(self,parent,label,row,column,bg_color):
Label.__init__(self,parent)
self['text']=label
self['justify']='right'
self['bg']=bg_color
self.grid(row=row,column=column, sticky=E)
Entry.__init__(self,parent)
self['width']="10"
self.grid(row=row,column=column+1)
This creates the configuration I want and is easy enough to arrange (I have them stored in a frame). The problem is I don't know how to access the Entry widgets that I have created as they are part of this new class.
I have a desire to read and delete the entries from the entry widgets. My best guess at clearing them was with this button that was being fed into the same frame:
class clearAllEntry(Button):
def clearAll(self,targetFrame):
targetFrame.labelEntry.Entry.delete(0,END)
def __init__(self,parent,targetFrame):
Button.__init__(self,parent,text='Clear All Entries',bg='black',fg='white')
self['command']= "clearAll(targetFrame)"
I have also looked at grid_slave as an approach but am having the same issue.
Any advice/help would be greatly appreciated.
First off, if you're creating a new class that contains two objects of different classes, you should not be using inheritance. Instead, use composition.
Second, to be able to access the entry widget, save it to an instance variable.
For example:
class LabelEntry():
def __init__(self, parent, label, row, column, bg_color):
self.label = Label(parent, text=label, justify='right', bg=bg_color)
self.entry = Entry(parent, width=10)
self.label.grid(row=row, column=column, sticky="e")
self.grid(row=row,column=column+1)
Later, you can reference these attributes like you can any other attribute:
le1 = LabelEntry(root)
...
print(le1.entry.get())
Related
I'm having some doubts with the design of mutiple inheritance in some Python classes.
The thing is that I wanted to extend the ttk button. This was my initial proposal (I'm omitting all the source code in methods for shortening, except init methods):
import tkinter as tk
import tkinter.ttk as ttk
class ImgButton(ttk.Button):
"""
This has all the behaviour for a button which has an image
"""
def __init__(self, master=None, **kw):
super().__init__(master, **kw)
self._img = kw.get('image')
def change_color(self, __=None):
"""
Changes the color of this widget randomly
:param __: the event, which is no needed
"""
pass
def get_style_name(self):
"""
Returns the specific style name applied for this widget
:return: the style name as a string
"""
pass
def set_background_color(self, color):
"""
Sets this widget's background color to that received as parameter
:param color: the color to be set
"""
pass
def get_background_color(self):
"""
Returns a string representing the background color of the widget
:return: the color of the widget
"""
pass
def change_highlight_style(self, __=None):
"""
Applies the highlight style for a color
:param __: the event, which is no needed
"""
pass
But I realized later that I wanted also a subclass of this ImgButton as follows:
import tkinter as tk
import tkinter.ttk as ttk
class MyButton(ImgButton):
"""
ImgButton with specifical purpose
"""
IMG_NAME = 'filename{}.jpg'
IMAGES_DIR = os.path.sep + os.path.sep.join(['home', 'user', 'myProjects', 'myProject', 'resources', 'images'])
UNKNOWN_IMG = os.path.sep.join([IMAGES_DIR, IMG_NAME.format(0)])
IMAGES = (lambda IMAGES_DIR=IMAGES_DIR, IMG_NAME=IMG_NAME: [os.path.sep.join([IMAGES_DIR, IMG_NAME.format(face)]) for face in [1,2,3,4,5] ])()
def change_image(self, __=None):
"""
Changes randomly the image in this MyButton
:param __: the event, which is no needed
"""
pass
def __init__(self, master=None, value=None, **kw):
# Default image when hidden or without value
current_img = PhotoImage(file=MyButton.UNKNOWN_IMG)
super().__init__(master, image=current_img, **kw)
if not value:
pass
elif not isinstance(value, (int, Die)):
pass
elif isinstance(value, MyValue):
self.myValue = value
elif isinstance(value, int):
self.myValue = MyValue(value)
else:
raise ValueError()
self.set_background_color('green')
self.bind('<Button-1>', self.change_image, add=True)
def select(self):
"""
Highlights this button as selected and changes its internal state
"""
pass
def toggleImage(self):
"""
Changes the image in this specific button for the next allowed for MyButton
"""
pass
The inheritance feels natural right to his point. The problem came when I noticed as well that most methods in ImgButton would be reusable for any Widget I may create in the future.
So I'm thinking about making a:
class MyWidget(ttk.Widget):
for putting in it all methods which help with color for widgets and then I need ImgButton to inherit both from MyWidget and ttk.Button:
class ImgButton(ttk.Button, MyWidget): ???
or
class ImgButton(MyWidget, ttk.Button): ???
Edited: Also I want my objects to be loggable, so I did this class:
class Loggable(object):
def __init__(self) -> None:
super().__init__()
self.__logger = None
self.__logger = self.get_logger()
self.debug = self.get_logger().debug
self.error = self.get_logger().error
self.critical = self.get_logger().critical
self.info = self.get_logger().info
self.warn = self.get_logger().warning
def get_logger(self):
if not self.__logger:
self.__logger = logging.getLogger(self.get_class())
return self.__logger
def get_class(self):
return self.__class__.__name__
So now:
class ImgButton(Loggable, ttk.Button, MyWidget): ???
or
class ImgButton(Loggable, MyWidget, ttk.Button): ???
or
class ImgButton(MyWidget, Loggable, ttk.Button): ???
# ... this could go on ...
I come from Java and I don't know best practices for multiple inheritance. I don't know how I should sort the parents in the best order or any other thing useful for designing this multiple inheritance.
I have searched about the topic and found a lot of resources explaining the MRO but nothing about how to correctly design a multiple inheritance. I don't know if even my design is wrongly made, but I thought it was feeling pretty natural.
I would be grateful for some advice, and for some links or resources on this topic as well.
Thank you very much.
I've been reading about multiple inheritance these days and I've learnt quite a lot of things. I have linked my sources, resources and references at the end.
My main and most detailed source has been the book "Fluent python", which I found available for free reading online.
This describes the method resolution order and design sceneries with multiple inheritance and the steps for doing it ok:
Identify and separate code for interfaces. The classes that define methods but not necessarily with implementations (these ones should be overriden). These are usually ABCs (Abstract Base Class). They define a type for the child class creating an "IS-A" relationship
Identify and separate code for mixins. A mixin is a class that should bring a bundle of related new method implementations to use in the child but does not define a proper type. An ABC could be a mixin by this definition, but not the reverse. The mixin doesn't define nor an interface, neither a type
When coming to use the ABCs or classes and the mixins inheriting, you should inherit from only one concrete superclass, and several ABCs or mixins:
Example:
class MyClass(MySuperClass, MyABC, MyMixin1, MyMixin2):
In my case:
class ImgButton(ttk.Button, MyWidget):
If some combination of classes is particularly useful or frequent, you should join them under a class definition with a descriptive name:
Example:
class Widget(BaseWidget, Pack, Grid, Place):
pass
I think Loggable would be a Mixin, because it gathers convenient implementations for a functionality, but does not define a real type. So:
class MyWidget(ttk.Widget, Loggable): # May be renamed to LoggableMixin
Favor object composition over inheritance: If you can think of any way of using a class by holding it in an attribute instead of extending it or inheriting from it, you should avoid inheritance.
"Fluent python" - (Chapter 12) in Google books
Super is super
Super is harmful
Other problems with super
Weird super behaviour
In principle, use of multiple inheritance increases complexity, so unless I am certain of its need, I would avoid it. From your post you already look aware of the use of super() and the MRO.
A common recommendation is to use composition instead of multiple inheritance, when possible.
Another one is to subclass from only one instantiable parent class, using abstract classes as the other parents. That is, they add methods to this subclass, but never get instantiated themselves. Just like the use of interfaces in Java. Those abstract classes are also called mixins, but their use (or abuse) is also debatable. See Mixins considered harmful.
As for your tkinter code, besides logger code indentation, I don't see a problem. Maybe widgets can have a logger instead of inheriting from it. I think with tkinter the danger is the unwanted override by mistake of one of the hundreds of available methods.
{Edit: the answer by Bryan Oakley in the suggested duplicate question enter link description here a) fires a response on change to the array variable (arrayvar.trace mode="w"), and I need it triggered on FocusOut, as described in my original question; b) works for Python 2, but I'm having trouble converting it to work in Python 3.5. I'm currently using his and pyfunc's answers as leads and trying to figure out a similar solution using a FocusOut event.}
I am working on a tkinter GUI that lets a user select a particular type of calculation, using a pair of radio button lists. Based on the selections, a tool bar is populated with multiple modular entry widgets, one for each variable the calculation requires. The goal is to have the numerical entry values passed to the model, which will return data to be graphed on a canvas or matplotlib widget.
My question is: what typical strategy is used for gathering and continually refreshing values from multiple widgets, in order to update displays and to pass them on to the model? The trick here is that there will be a large number of possible calculation types, each with their own toolbar. I'd like the active toolbar to be "aware" of its contents, and ping the model on every change to a widget entry.
I think the widgets and the toolbar would have to be classes, where the toolbar can query each widget for a fresh copy of its entry values when a change is detected, and store them as some collection that is passed to the model. I'm not entirely sure how it can track changes to the widgets. Using a "validate='focusout' " validation on the entry widget (e.g. as in
this validation reference )
suggests itself, but I already use "validate='key' " to limit all entries to numbers. I don't want to use "validate=all" and piggyback onto it because I don't want to continually ask the model to do a lengthy calculation on every keypress.
I'm new to GUI programming, however, so I may be barking up the wrong tree. I'm sure there must be a standard design pattern to address this, but I haven't found it.
Below is a screenshot of a mockup to illustrate what I want the GUI to do. The Task radiobutton controls which secondary button menu appears below. The selection in the second menu populates the top toolbar with the necessary entry widgets.
The following code does (mostly) what I want. The ToolBar frame objects will store the values from its contained widgets, and call the appropriate model as needed. The VarBox objects are Entry widgets with extra functionality. Hitting Tab or Return refreshes the data stored in the ToolBar dictionary, tells the ToolBar to send data to the model, and shifts focus to the next VarBox widget.
from tkinter import *
# Actual model would be imported. "Dummy" model for testing below.
def dummy_model(dic):
"""
A "dummy" model for testing the ability for a toolbar to ping the model.
Argument:
-dic: a dictionary whose values are numbers.
Result:
-prints the sum of dic's values.
"""
total = 0
for value in dic.values():
total += value
print('The total of the entries is: ', total)
class ToolBar(Frame):
"""
A frame object that contains entry widgets, a dictionary of
their current contents, and a function to call the appropriate model.
"""
def __init__(self, parent=None, **options):
Frame.__init__(self, parent, **options)
self.vars = {}
def call_model(self):
print('Sending to dummy_model: ', self.vars)
dummy_model(self.vars)
class VarBox(Frame):
"""
A customized Frame containing a numerical entry box
Arguments:
-name: Name of the variable; appears above the entry box
-default: default value in entry
"""
def __init__(self, parent=None, name='', default=0.00, **options):
Frame.__init__(self, parent, relief=RIDGE, borderwidth=1, **options)
Label(self, text=name).pack(side=TOP)
self.widgetName = name # will be key in dictionary
# Entries will be limited to numerical
ent = Entry(self, validate='key') # check for number on keypress
ent.pack(side=TOP, fill=X)
self.value = StringVar()
ent.config(textvariable=self.value)
self.value.set(str(default))
ent.bind('<Return>', lambda event: self.to_dict(event))
ent.bind('<FocusOut>', lambda event: self.to_dict(event))
# check on each keypress if new result will be a number
ent['validatecommand'] = (self.register(self.is_number), '%P')
# sound 'bell' if bad keypress
ent['invalidcommand'] = 'bell'
#staticmethod
def is_number(entry):
"""
tests to see if entry is acceptable (either empty, or able to be
converted to a float.)
"""
if not entry:
return True # Empty string: OK if entire entry deleted
try:
float(entry)
return True
except ValueError:
return False
def to_dict(self, event):
"""
On event: Records widget's status to the container's dictionary of
values, fills the entry with 0.00 if it was empty, tells the container
to send data to the model, and shifts focus to the next entry box (after
Return or Tab).
"""
if not self.value.get(): # if entry left blank,
self.value.set(0.00) # fill it with zero
# Add the widget's status to the container's dictionary
self.master.vars[self.widgetName] = float(self.value.get())
self.master.call_model()
event.widget.tk_focusNext().focus()
root = Tk() # create app window
BarParentFrame = ToolBar(root) # holds individual toolbar frames
BarParentFrame.pack(side=TOP)
BarParentFrame.widgetName = 'BarParentFrame'
# Pad out rest of window for visual effect
SpaceFiller = Canvas(root, width=800, height=600, bg='beige')
SpaceFiller.pack(expand=YES, fill=BOTH)
Label(BarParentFrame, text='placeholder').pack(expand=NO, fill=X)
A = VarBox(BarParentFrame, name='A', default=5.00)
A.pack(side=LEFT)
B = VarBox(BarParentFrame, name='B', default=3.00)
B.pack(side=LEFT)
root.mainloop()
I am doing a project for school, but I am stuck at the point of the creation of multiple checkboxes. I copy/pasted some code and messed around with it, trying to get two checkboxes, but I was unable to do so. The checkboxes use the same variable to tick/untick (I know this). I am unable to find a way to get a second variable to allow the second checkbox to be ticked or unticked depending on whether the first is unticked or ticked.
from Tkinter import *
root = Tk()
root.title("Payroll")
root.resizable(width=FALSE, height=FALSE)
class App:
def __init__(self,master):
root.minsize(width=500, height=500)
root.maxsize(width=500, height=500)
self.var = IntVar()
frame = Frame(master)
frame.grid()
f2 = Frame(master,width=200,height=100)
f2.grid(row=0,column=1)
btnSalary = Checkbutton(frame,text='Salary',variable=self.var,command=self.fx)
btnWage = Checkbutton(frame,text='Wage',variable=self.var,command= self.fx)
btnSalary.grid(row=0,column=0)
btnWage.grid(row=2,column=0)
msg1="Wage"
self.v= Message(f2,text=msg1)
msg2="Salary"
self.v= Message(f2,text=msg2)
def fx(self):
if self.var.get():
self.v.grid(column=1,row=0,sticky=N)
else:
self.v.grid_remove()
app = App(root)
root.mainloop()
Checkbuttons and Radiobuttons have very specific use cases:
You would use one or more Checkbuttons when the user can check none, any, or all of the options. A checkbutton always has two values, an onvalue and an offvalue (typically True and False, answering the question "is it checked").
You would use one or more Radiobuttons when the user can pick exactly one choice from a set of choices.
If your case it looks like you're wanting the user to choose either Salary or Wage. If that is true, you need to use Radiobuttons, not Checkbuttons. When you use Radiobuttons, all of the choices that act as one must use the same textvariable. This is how Tkinter knows they work as a set. Also, you must give each Radiobutton a distinct value, so that when you get the value you will know which one is selected.
In your case, the widgets might look something like this:
btnSalary = Radiobutton(..., variable=self.var,value="salary", ...)
btnWage = Radiobutton(...,variable=self.var, value="wage", ...)
This might be a very uninformed question.
I've been trying to figure out QGraphics*, and have run into a problem when trying to move an item (a pixmap) relative to or inside of the QGraphicsView.
class MainWindow(QMainWindow,myProgram.Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.scene = QGraphicsScene()
self.graphicsView.setScene(self.scene)
pic = QPixmap('myPic.png')
self.scene.addPixmap(pic)
print(self.scene.items())
This is the relevant part of the program with an arbitrary PNG
My goal, for example, would be to move the trash-can to the left-most part of the QGraphicsView.
I tried appending this to the code above:
pics = self.scene.items()
for i in pics:
i.setPos(100,100)
However, it doesn't make a difference, and even if it did, it would be awkward to have to search for it with a "for in".
So my questions are:
How do I access a QPixmap item once I have added it to a QGraphicsView via a QGraphicsScene. (I believe The QPixmap is at this point a QGraphicsItem, or more precisely a QGraphicsPixmapItem.)
Once accessed, how do I move it, or change any of its other attributes.
Would be very grateful for help, or if anyone has any good links to tutorials relevant to the question. :)
The problem is that you need to set the size of your scene and then set positions of the items, check this example:
from PyQt4 import QtGui as gui
app = gui.QApplication([])
pm = gui.QPixmap("icon.png")
scene = gui.QGraphicsScene()
scene.setSceneRect(0, 0, 200, 100) #set the size of the scene
view = gui.QGraphicsView()
view.setScene(scene)
item1 = scene.addPixmap(pm) #you get a reference of the item just added
item1.setPos(0,100-item1.boundingRect().height()) #now sets the position
view.show()
app.exec_()
I'm trying to populate a table (present in the main window) from a slider that's located in a widget in a separate class. I can't seem to get it to work...what's the best way to go about doing this?
Here's my current code:
class Widget(QWidget):
def __init__(self,filename,parent=None):
super(Widget,self).__init__(parent)
self.resize(900,900)
self.layout=QVBoxLayout(self)
frame=Frame(filename)
self.image=pg.ImageView()
self.image.setImage(frame.data)
self.image.setCurrentIndex(0)
fileheader=FileHeader(filename)
self.slider=QSlider(self)
self.slider.setOrientation(Qt.Horizontal)
self.slider.setMinimum(1)
self.slider.setMaximum(fileheader.numframes)
self.slider.sliderMoved.connect(self.sliderMoved)
self.layout.addWidget(self.image)
self.layout.addWidget(self.slider)
def sliderMoved(self,val):
print "slider moved to:", val
fileheader=FileHeader(filename)
idx=val
frame=fileheader.frameAtIndex(idx)
self.image.setImage(frame.data)
class MainWindow(QMainWindow):
def __init__(self, filename, parent=None):
super(MainWindow,self).__init__(parent)
self.initUI(filename)
def initUI(self,filename):
self.filetable=QTableWidget()
self.frametable=QTableWidget()
self.imageBrowser=Widget(filename)
self.imagesplitter=QSplitter(Qt.Horizontal)
self.tablesplitter=QSplitter(Qt.Horizontal)
self.imagesplitter.addWidget(self.imageBrowser)
self.tablesplitter.addWidget(self.imagesplitter)
self.tablesplitter.addWidget(self.filetable)
self.tablesplitter.addWidget(self.frametable)
self.setCentralWidget(self.tablesplitter)
exitAction=QAction(QIcon('exit.png'),'&Exit',self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(qApp.quit)
openAction=QAction(QIcon('open.png'),'&Open',self)
openAction.setShortcut('Ctrl+O')
menubar=self.menuBar()
fileMenu=menubar.addMenu('&File')
fileMenu.addAction(exitAction)
fileMenu.addAction(openAction)
self.fileheader=FileHeader(filename)
self.connect(self.frametable,
SIGNAL("Widget.sliderMoved(idx)"),
self.fileheader.frameAtIndex(idx))
self.frameheader=self.fileheader.frameAtIndex(0)
self.populate()
def populate(self):
self.filetable.setRowCount(len(self.fileheader.fileheader_fields))
self.filetable.setColumnCount(2)
self.filetable.setHorizontalHeaderLabels(['File Header','value'])
for i,field in enumerate(self.fileheader.fileheader_fields):
name=QTableWidgetItem(field)
value=QTableWidgetItem(unicode(getattr(self.fileheader,field)))
self.filetable.setItem(i,0,name)
self.filetable.setItem(i,1,value)
self.frametable.setRowCount(len(self.frameheader.frameheader_fields))
self.frametable.setColumnCount(2)
self.frametable.setHorizontalHeaderLabels(['Frame Header','Value'])
for i,fields in enumerate(self.frameheader.frameheader_fields):
Name=QTableWidgetItem(fields)
Value=QTableWidgetItem(unicode(getattr(self.frameheader,fields)))
self.frametable.setItem(i,0,Name)
self.frametable.setItem(i,1,Value)
I know the "connect" is wrong-- I'm very new to PyQt and Python in general, so I'm not quite sure where to start.
Since self.imageBrowser is your Widget class, it will have the slider attribute which has the sliderMoved signal. You just need a few more dots.
self.imageBrowser.slider.sliderMoved.connect(self.fileheader.frameAtIndex)
The way you have it organized is correct though. Your main window composes your custom widgets and binds the connections together.
Though because you have a data source, and also a QTableWidget that will need to be updated, you probably need to wrap the steps up into a little method:
def initUI(self,filename):
...
self.imageBrowser.slider.sliderMoved.connect(self._handle_slider_moved)
# initialize it the first time during the window set up
self._handle_slider_moved(0)
def _handle_slider_moved(self, val):
# update the data source
self.fileheader.frameAtIndex(val)
# update the second data source
self.frameheader=self.fileheader.frameAtIndex(0)
# now refresh the tables
self.populate()