I have some items in a list which decrease a internal value, when this value is 0, a windows pops and ask for what to do, there are 3 options, set the item as 'completed', set the item as 'missed', set the item as 'delayed'.
The window is a QDockWidget and options are selected via QPushButtons, I want to connect them to a function that will deal with each of the 3 actions possible.
like
self.options_button_completed.clicked.connect(self.set_completed)
self.options_button_missed.clicked.connect(self.set_missed)
self.options_button_delayed.clicked.connect(self.set_delayed)
But I can't do this way because I need the reference to the item that raised the window in the first place
I wonder if it's possible to set the clicked slot in a way that it will also pass a extra argument, the item who raised the QDockWidget.
Is it possible? Or else, what's the proper way to deal with this?
I assume that I would need to keep a variable with the item, but I'm looking for a more clean way, without clogging the class with variables.
By making the Window a separated QWidget I'm able to instantiate it in the main window and pass and extra argument(item) which will be a instance attribute.
class MainFrame(QWidget):
def __init__(self):
self.popup_windows = [] # to store the pops
def display_popup_window(self, item):
# item is the reference item that it's internal value reached 0
popup_window = PopupFrame(self, item)
popup_window.show()
popup_window._raise()
self.popup_windows.append(popup_window)
class PopupFrame(QWidget):
def __init__(self, parent, item):
self.parent = parent
self.item = item
# set up other things, like buttons, layout...
self.options_button_completed.clicked.connect(self.set_completed)
self.options_button_missed.clicked.connect(self.set_missed)
self.options_button_delayed.clicked.connect(self.set_delayed)
def set_completed(self):
# do something with self.item
pass
It's simplified just to convey the general idea, if anyone need a working example, feel free to ask in the comments and I'll provided.
Related
In a QlistWidget, When using listItem.setIcon(qIcon) the icon is put on the left of the list item. How can I make it show up on the right as shown below?
Also another question. How can I remove the icon from the item?
This can be done quite easily with a simple custom item-delegate:
class ItemDelegate(QtGui.QStyledItemDelegate):
def paint(self, painter, option, index):
option.decorationPosition = QtGui.QStyleOptionViewItem.Right
super(ItemDelegate, self).paint(painter, option, index)
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
...
self.delegate = ItemDelegate()
self.listWidget.setItemDelegate(self.delegate)
To remove an icon from an item, just set it to a null QIcon:
listItem.setIcon(QtGui.QIcon())
For this you will need to provide your own item delegate that draws the item data like you want.
See QAbstractItemView::setItemDelegate().
You can likely use QStyledItemDelegate as your base class and let its paint() handle all aspects other than the Qt::DecorationRole (the icon) and draw that yourself.
I'm making a GUI with pyqt4 and python. Right now I have a QLineEdit and QComboBox, where the QLineEdit displays the values and the QComboBox can be used to change units. I'm using signals and slots to handle real time unit/value feedback for the user but I'm having problems understanding how to programmatically work with the values as I need them all to be in standard units. Here's what I've got so far, the combo_box_line_edit_list is a list of list where I wrap the combo box and line list together
class UnitConverterSignaler(QtCore.QObject):
def __init__(self, combo_box_line_edit_list):
super(QtCore.QObject, self).__init__()
self.combo_box_line_edit_list = combo_box_line_edit_list
self.combo_box_list = [line_edit_combo_box[0] for line_edit_combo_box in combo_box_line_edit_list]
for combo_box, line_edit in self.combo_box_line_edit_list:
combo_box.currentIndexChanged['QString'].connect(line_edit.convert_units)
line_edit.store_unit_state(combo_box.currentText())
line_edit.standard_unit = combo_box.itemText(1)
def convert_to_standard(self):
for combo_box in self.combo_box_list:
combo_box.setCurrentIndex(0)
def convert_to_international(self):
for combo_box in self.combo_box_list:
combo_box.setCurrentIndex(1)
def toggle_unit_conversion(self, hold_line_values_steady):
for combo_box in self.combo_box_list:
if hold_line_values_steady:
combo_box.do_not_convert_units_on_change()
else:
combo_box.convert_units_on_change()
def convert_units_on_change(self):
"""
Changes the value of the line edit each time the combo box is changed
"""
for combo_box, line_edit in self.combo_box_line_edit_list:
combo_box.currentIndexChanged['QString'].connect(line_edit.convert_units)
combo_box.currentIndexChanged['QString'].disconnect(line_edit.store_unit_state)
def do_not_convert_units_on_change(self):
"""
Holds the line edit value constant in spite of combo box changes
"""
for combo_box, line_edit in self.combo_box_line_edit_list:
combo_box.currentIndexChanged['QString'].disconnect(line_edit.convert_units)
combo_box.currentIndexChanged['QString'].connect(line_edit.store_unit_state)
Instantiated & used in another class
self.lockCellCheckBox.toggled.connect(self.unit_converter_signaler.toggle_unit_conversion)
self.internationalPushButton.clicked.connect(self.unit_converter_signaler.convert_to_international)
self.standardPushButton.clicked.connect(self.unit_converter_signaler.convert_to_standard)
I've also monkey patched the QLineEdit instead of subclassing so I can make quick changes with QtDesigner.
# monkey patch slot onto line_edit
def convert_units(line_edit, end_unit):
converted_unit_value = line_edit.unit_registry.convert(float(line_edit.text()), line_edit.stored_unit_state, str(end_unit))
line_edit.setText(str(converted_unit_value))
line_edit.stored_unit_state = str(end_unit)
# monkey patch slot onto line_edit
def store_unit_state(line_edit, unit):
line_edit.stored_unit_state = str(unit)
Would the most generalized way to get the standard units out in my main program be the creation of a signal for each combo box/line edit in the UnitConverter?
From what I understood so far: you have many combo-box/line-edit pairs and the entered values should always be converted to standard units (e.g. displayed on a third QLabel or whatever).
Would the most generalized way to get the standard units out in my main program be the creation of a signal for each combo box/line edit in the UnitConverter?
No, you don't have to. A slot in python (or especially in pyqt) can be any callable object. A callable object is an object with method __call__(self) implemented.
Therefore I would suggest you to create a class which takes the related object(s) as parameter(s) in the contructor and changes them in __call__(self). Something like this:
class ConverterSignal:
def __init__(whatever_you_want_to_refer_to):
self.whatever_you_want_to_refer_to = whatever_you_want_to_refer_to
def __call(self)__:
""" Here you can refer to whatever_you_want_to_refer_to and do whatever you want with it """
The connection is done as following (for the combo box as an example):
self.connect(combo_box, QtCore.SIGNAL('activated(int)'), ConverterSignal(whatever_you_want_to_refer_to))
Here an instance of the class ConverterSignal is created and will be called if the corresponding signal is emitted.
I'm trying to understand how to logically separate CRUD responsibilities so to adhere to the Single Responsibility Principle (SRP).
As I understand the definition of SRP, a single responsibility may not necessarily be a single behavior, but instead be a collection of behaviors with a well-defined, logical boundary from others.
In my example, RestaurantMenu is nothing more than a collection. I understand that there are more efficient ways to represent this, such as with a dictionary, but that is beyond the intent of this example. My RestaurantMenu has no behavior assigned to it because it remains unclear to me as to whether defining any further behavior by it would breach the SRP. It feels rather uncomfortable instantiating and calling separate CRUD objects through a Manager object rather than through methods in RestaurantMenu, so that is why I've decided to ask the audience here for some guidance.
Does the following example pass the SRP litmus test?
class RestaurantMenu(object):
def __init__(self, title, creator, catalog_type, restaurant):
self._title = title
self._creator = creator
self._catalog_type = catalog_type
self._restaurant = restaurant
self._menuitems = dict()
class MenuManager(object):
"""Responsibility
--------------
Coordinates CRUD related activities with a menu
"""
def __init__(self, menu):
self._menu = menu
def add_menu_item(self, item, value):
menu_item_adder = AddMenuItem(self._menu)
menu_item_adder(item, value)
def del_menu_item(self, item):
menu_item_deleter = DelMenuItem(self._menu)
menu_item_deleter(item)
def update_menu_item(self, existing_item, new_info):
menu_item_updater = UpdateMenuItem(self._menu)
menu_item_updater(existing_item, new_info)
def get_menu_items(self):
menu_item_getter = GetMenuItems(self._menu)
menu_item_getter()
class GetMenuItems(object):
def __init__(self, menu):
self._menu = menu
def __call__(self):
print(self._menu._title)
print('='*len(self._menu._title))
for key, value in self._menu._menuitems.items():
print(key, value)
class AddMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, item, value):
if item not in self._menu._menuitems:
self._menu._menuitems[item] = value
print('Item added:', item)
else:
print('Item already exists. Please update instead.')
class DelMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, item):
popped = self._menu._menuitems.pop(item)
print('Item removed:', popped)
class UpdateMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, existing_item, new_info):
self._menu._menuitems.update(existing_item=new_info)
print('Item updated:', existing_item, ' with', new_info)
def main():
mymenu = RestaurantMenu("Joe's Crab Shack 2014 Menu",
"Joe Schmoe",
"Restaurant",
"Joe's Crab Shack")
menumanager = MenuManager(mymenu)
menumanager.add_menu_item('longneck_clams', 7.00)
menumanager.add_menu_item('1 pound lobster', 15.00)
menumanager.add_menu_item('lobster chowder', 9.00)
print('-'*50)
menumanager.get_menu_items()
if __name__ == "__main__":
main()
One possible definition of SRP compliance is that there should only be one reason for a class to change.
This makes it very hard to call SRP or not on a piece of code in the abstract -- it basically depends on what will happen to evolve together and separately over time in your application.
Generally speaking though, the UI is one of the primary things that might evolve independently from other parts of a program. Users will keep wanting to make little display adjustments over the course of a project, and it's a nice thing to be able to modify the presentation logic without fearing to break the rest of the system. Persistence is another thing you might want to change, either as a result of new architectural decisions or temporarily, depending on the context (swapping in dummy persistence objects in tests for instance).
This is why in most real-world applications, I would tend to split up classes by technical responsibility rather than business operations on a same entity like C/R/U/D.
If you look closely at your current implementation, you'll notice patterns in your classes. They all fiddle with a MenuManager and the MenuItems stored in it. They all print things to the screen.
If you want to change something in the way data is displayed or stored, you'll basically have to touch all these classes. I'm not saying it's a serious flaw in the case of a small simple system like this, but in a larger application it might well be a problem.
Put another way, your example makes it easy to have menu updates done through a graphical interface into a SQL database, menu inserts done via a command shell into flat files, and menu reads spitting out an XML file with data gathered from a web service. This might be what you want to do in very particular circumstances, but not most of the time...
I just want to complement #guillaume31 answer, but I don't think it will fit in a comment.
As I understand the definition of SRP, a single responsibility may not necessarily be a single behavior, but instead be a collection of behaviors with a well-defined, logical boundary from others.
However you say you understand this, your code shows the opposite. You've spread a high cohesive group of tasks through several classes. Why this is bad?
How many times do you have the following code?
def __init__(self, menu):
self._menu = menu
I'm to lazy to count it, but you'll notice that this is an unecessary code duplication.
In this particular simple case, there is no problem, in deed, but if you application grow, you'll have a huge headache.
In some countries, it's valentine's day tomorrow, so you should remember how to KISS.
i've recently come across a problem thats bugging me with the tkinter entry .get() function, I have put together an example code so you can see what i'm trying to do, I have two classes, a class for each window. In the first window(main window) I have an entry box, in the second window I am attempting to get the entry box text from the first window.
Here's the code: (Trying to get entry box info from the first class in the second class)
from Tkinter import *
class window_1(object):
def __init__(self):
self.app = Tk()
self.app.title("Window One")
def entrybox(self):
self.ent = Entry(self.app) #This is the text i'm trying to get in 2nd class
def button(self):
def ODV(self):
class window_2(object):
def __init__(self):
self.app2 = Tk()
self.app2.title("Window Two")
def labels(self):
self.label_0 = Label(self.app2, text = "Name: ")
def info(self):
self.fetch_name = self.ent.get()#Here is my problem
def gridder(self):
self.label_0.grid(row = 0, column = 0)
self.fetch_name.grid(row = 0, column = 1)
rooter = window_2()
rooter.labels()
rooter.info()
rooter.gridder()
open_data_viewer = lambda: ODV(self)
self.but = Button(self.app, text = "Save", command = open_data_viewer)
def packer(self):
self.ent.pack(anchor = W)
self.but.pack(anchor = W)
def App_Runner(self):
self.app.mainloop()
root = window_1()
root.entrybox()
root.button()
root.packer()
root.App_Runner()
Your first problem is that you're creating more than one instance of Tk. You can't do that, tkinter isn't designed to work that way. If you want multiple windows, create instances of Toplevel. A tkinter program should always have exactly one instance of Tk, exactly one call to mainloop, and there should be little to no code following the call to mainloop.
Second, there is absolutely no value in embedding the definition of a class inside a function. Move it out, it will make your code easier to understand, and easier to write and maintain.
Third, for an instance of one object to access a method on another object, the first object needs to know about the second object or needs to know about a central "controller" object. This isn't a tkinter problem, it's a normal thing to consider when writing OO code.
From a practical standpoint, you need to pass in a reference either to the entry widget, or the object that contains the entry widget, when you create the second object. For example:
class window_2(object):
def __init__(self, other):
...
self.other = other
...
def info(self):
self.fetch_name = self.other.ent.get()
...
rooter = window_2(self) # pass "self" to the new object
This produces a tight coupling between the two objects -- the second object knows about the inner workings of the first object. This is not very good design, though for very, very simple programs it's not so bad. The problem is this: if you change the layout of the first widget, perhaps renaming "self.ent" to "self.some_other_frame.ent", you have to modify the other class too.
A better solution is to define in your first class a function that gets it's own value. Of course, ent serves that purpose, but again, that is a tight coupling. better to have a helper function:
class window_1(object):
...
def get_string(self):
return self.ent.get()
class window_2(object):
def info(self):
self.fetch_name = self.other.get_string()
This still has a loose coupling, but one that is much easier to manage because the coupling isn't tied to the specific internal layout and names of the first window. You can change the widgets all you want, as long as you continue to provide a get_string method that does what the other class expects. Your first class is providing a contract to the second class: a promise that no matter how else the window may change over time, it promises to provide this interface.
QLineEdit has a textEdited signal which is emitted whenever the text is changed by user interaction, but not when the text is changed programatically. However, QDateTimeEdit has only a general dateTimeChanged signal that does not distinguish between these two types of changes. Since my app depends on knowing if the field was edited by the user or not, I'm looking for ways to implement it.
My (currently working) strategy was to create an eventFilter to the edit field, intercept key press and mouse wheel events and use them to determine if the field was modified by the user (storing this info in an object), and finally connecting the dateTimeChanged signal to a function that decides if the change was by the user or done programatically. Here are the relevant parts of the code (python):
class UserFilter(QObject):
def __init__(self, parent):
QObject.__init__(self, parent)
self.parent = parent
def eventFilter(self, object, event):
if event.type() == QEvent.KeyPress or event.type() == QEvent.Wheel:
self.parent.edited = True
else:
pass
return False
class DockThumb(QWidget):
def __init__(self, parent):
QWidget.__init__(self, parent)
self.parent = parent
self.edited = False
self.dateedit = QDateTimeEdit(self)
self.userfilter = UserFilter(self)
self.dateedit.installEventFilter(self.userfilter)
...
self.connect(self.dateedit,
SIGNAL('dateTimeChanged(QDateTime)'),
self.edited_or_not)
def edited_or_not(self):
if self.edited:
# User interacted! Go for it.
self.parent.runtimer()
# self.edited returns to False once data is saved.
else:
# User did not edited. Wait.
pass
Is there a more objective way of doing it? I tried subclasssing QDateTimeEdit, but failed to deal with events... Expected user interactions are direct typing, up/down arrow keys to spin through dates and copy/pasting the whole string.
The idiomatic Qt way of achieving this is indeed subclassing QDateTimeEdit and adding the functionality you require. I understand you tried it and "failed to deal with events", but that's a separate issue, and perhaps you should describe those problems - since they should be solvable.
Since I'm not entirely sure about what you are trying to do, I would agree with Eli Bendersky. Short of that, if you know when you will be programatically changing the QDateTimeEdit, set some flag that you can check in the slot handler that will indicate a programatic change is occurring and clear it when you are done.