How do I catch the PyQt5 QSplitter.resizeEvent? - python

I can catch the user resizing or repositioning the window simply by defining functions:
def resizeEvent(self, event): ...
def moveEvent(self, event): ...
But I also have 2 QSplitter, and would like to know any new split the user has applied to make it also a default for the next start. QSplitter also has a resizeEvent(), but I can't catch it, because it is already used by the apparently higher ranking functions above.
How can I get hold of QSplitter's resizeEvent?
I now use as a workaround def paintEvent(self, event):... which does allow me to get the needed info, but it feels a bit clumsy :-/

Related

What are the responsibilities of size_allocate() in a custom container class? How does it relate to adjust_size_allocation()?

I try to create a proper container Class for Gtk in Python (MyBin in the code below). There seems to be no authoritative documentation on how to do that, only bits and pieces, which I glued together with trial and error. – There are many open questions, but let me focus on size_allocate() here:
What is the the original size_allocate() function doing? And what is my overlaid version in my container class supposed to do?
I already do 95% know that I have to replace the original method with my own by prepending “do_” to form the do_size_allocate() method (by trial and error – see code below; I could not find anything talking about all those “do_*” functions).
I found so far, that calling self.set_allocation(allocation) seems to be a good idea as is self.get_child().size_allocate(...) to give the child some working space (see code). – Any other obligations I have to fulfill? What is the original function doing additionally?
Regarding the allocation I pass down to the child: I need to adapt it (not in the code below yet). I successfully(?) used a fresh Gdk.Rectangle() on which I set x, y, width and height, but I somehow feel that it’s wrong and something with (do_)adjust_size_allocation() should be used instead. But how?
Lets look into the documentation of do_size_allocate():
This function is only used by Gtk.Container subclasses, to assign a
size and position to their child widgets. [“Only used”? Only called from? Only implemented at? Only overwritten by? – Well, I’m doing it already, but the doc wasn’t helpful in finding out how.]
In this function, the allocation may be adjusted. [How?] It will be forced to
a 1x1 minimum size [Can confirm only that get_allocation() will return a 1x1 if I don’t set anything.],and the adjust_size_allocation virtual method on
the child will be used to adjust the allocation. [What? For my own allocation? Or for the one I set on the child via ...get_child().size_allocate()? Who calls that adjust method? In which conditions is it not called before size_allocate()?] Standard adjustments
include removing the widget’s margins, and applying the widget’s
Gtk.Widget :halign and Gtk.Widget :valign properties.
#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class MyBin(Gtk.Bin):
def __init__(self):
super().__init__()
def do_size_allocate(self, allocation):
print('PARENT METHOD do_size_allocate', allocation.width)
# if not called, `get_allocation()` in `do_draw()` will return a 1x1 rectangle
self.set_allocation(allocation)
# strangely required so that `do_draw()` will be called even on this class!
self.get_child().size_allocate(allocation)
def do_draw(self, c):
allocation = self.get_allocation()
print('PARENT do_draw()', allocation.width)
self.propagate_draw(self.get_child(), c)
class MyChild(Gtk.Button):
def __init__(self):
super().__init__()
self.connect('size-allocate', self.size_allocate_handler)
self.connect('draw', self.draw_handler)
def size_allocate_handler(self, self2, allocation):
print('CHILD signal size-allocate', allocation.width)
def draw_handler(self, self2, c):
allocation = self.get_allocation()
print('CHILD signal draw', allocation.width)
class MainWindow(Gtk.Window):
def __init__(self):
super().__init__(title='d2see test pattern')
the_child = MyChild()
my_container = MyBin()
my_container.add(the_child)
self.add(my_container)
self.show_all()
if __name__ == '__main__':
MainWindow()
Gtk.main()

Sub frame does not destroy in python

I have a program that I'm trying to develop but I guess I don't know the proper way to make subframes. I've looked at several examples and can't seem to get it to destroy the frame when it's closed. I get the following error readout (using pycharm):
Exception wx._core.PyDeadObjectError: PyDeadObjectError('The C++ part of the
Choice object has been deleted, attribute access no longer allowed.',) in
<bound method pvFileINdialog.__del__ of <fileIO.pvFileIOGUIFileIOdialog.pvFileINdialog;
proxy of <Swig Object of type 'wxDialog *' at 0x340cc70> >> ignored
Although the interpreter takes care of the problem, I would like to learn the proper way to take care of it.
If I move the destroy and close commands around no combination changes the result.
The code of interest is:
#in MainFrame.py:
def fileimport(self, event):
importprompt = pvFileIOGUIFileIOdialog.pvFileINdialog(self)
importprompt.ShowModal()
importprompt.Destroy()
#referring to pvFileIOGUIFileIOdialog.pvFileINdialog :
class pvFileINdialog(pvFileIOGUI.FileINdialog):
def __init__(self, child):
pvFileIOGUI.FileINdialog.__init__(self, child)
#which refers to pvFileIOGUI.FileINdialog :
class FileINdialog(wx.Dialog):
def __init__(self, child):
wx.Dialog.__init__(self, child, id=wx.ID_ANY, title=u"Select Import Method...", pos=wx.DefaultPosition,
size=wx.Size(800, 600), style=wx.DEFAULT_DIALOG_STYLE)
#and is closed by this routine in pvFileIOGUIFileIOdialog.py:
def cancelclk(self, event):
self.Close()
return 0
It's difficult to understand what goes on because your error message refers to a Choice, but your code doesn't show any choices at all, so something is probably missing, i.e. the problem is probably in the part which you don't show. But in any case, calling Destroy() is not necessary and you shouldn't do it unless you have some real reason (do you?). So I'd start by removing all calls to Destroy() from your code.

Cannot add item to itemgroups if itemchange() is defined (PyQt)

I'm building a PyQt QGraphicsView project where some QGraphicItems can be moved around between different QGraphicsItemGroups. For that, I use the addItemToGroup() method of the "new" parent itemGroup.
This works fine, but only so long as I do not define the itemChange() method in my custom child-item class. Once I define that method (even if I just pass the function call to the super class), the childItems will not be added to ItemGroups no matter what I try.
class MyChildItem(QtGui.QGraphicsItemGroup):
def itemChange(self, change, value):
# TODO: Do something for certain cases of ItemPositionChange
return QtGui.QGraphicsItemGroup.itemChange(self, change, value)
#return super().itemChange(change, value) # Tried this variation too
#return value # Tried this too, should work according to QT doc
Am I simply too stupid for properly calling a superclass method in Python, or is the problem somewhere in the QT / PyQT magic?
I use Python 3.3 with PyQt 4.8 and QT 5.
I had the exact same problem. Maybe this: http://www.mail-archive.com/pyqt#riverbankcomputing.com/msg27457.html answers some of your questions?
Seems like we might be out of luck in PyQt4.
Update:
Actually, just found a workaround:
import sip
def itemChange(self, change, value):
# do stuff here...
result = super(TestItem, self).itemChange(change, value)
if isinstance(result, QtGui.QGraphicsItem):
result = sip.cast(result, QtGui.QGraphicsItem)
return result
taken from here: http://www.mail-archive.com/pyqt#riverbankcomputing.com/msg26190.html
Maybe not the most elegant and general solution, but over here, it works -- I'm able to add QGraphicItems to QGraphicItemGroups again.

Implement a userEdited signal to QDateTimeEdit?

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.

How would you design a very "Pythonic" UI framework?

I have been playing with the Ruby library "shoes". Basically you can write a GUI application in the following way:
Shoes.app do
t = para "Not clicked!"
button "The Label" do
alert "You clicked the button!" # when clicked, make an alert
t.replace "Clicked!" # ..and replace the label's text
end
end
This made me think - how would I design a similarly nice-to-use GUI framework in Python? One that doesn't have the usual tyings of basically being wrappers to a C* library (In the case of GTK, Tk, wx, QT etc etc)
Shoes takes things from web devlopment (like #f0c2f0 style colour notation, CSS layout techniques, like :margin => 10), and from ruby (extensively using blocks in sensible ways)
Python's lack of "rubyish blocks" makes a (metaphorically)-direct port impossible:
def Shoeless(Shoes.app):
self.t = para("Not clicked!")
def on_click_func(self):
alert("You clicked the button!")
self.t.replace("clicked!")
b = button("The label", click=self.on_click_func)
No where near as clean, and wouldn't be nearly as flexible, and I'm not even sure if it would be implementable.
Using decorators seems like an interesting way to map blocks of code to a specific action:
class BaseControl:
def __init__(self):
self.func = None
def clicked(self, func):
self.func = func
def __call__(self):
if self.func is not None:
self.func()
class Button(BaseControl):
pass
class Label(BaseControl):
pass
# The actual applications code (that the end-user would write)
class MyApp:
ok = Button()
la = Label()
#ok.clicked
def clickeryHappened():
print "OK Clicked!"
if __name__ == '__main__':
a = MyApp()
a.ok() # trigger the clicked action
Basically the decorator function stores the function, then when the action occurred (say, a click) the appropriate function would be executed.
The scope of various stuff (say, the la label in the above example) could be rather complicated, but it seems doable in a fairly neat manner..
You could actually pull this off, but it would require using metaclasses, which are deep magic (there be dragons). If you want an intro to metaclasses, there's a series of articles from IBM which manage to introduce the ideas without melting your brain.
The source code from an ORM like SQLObject might help, too, since it uses this same kind of declarative syntax.
I was never satisfied with David Mertz's articles at IBM on metaclsses so I recently wrote my own metaclass article. Enjoy.
This is extremely contrived and not pythonic at all, but here's my attempt at a semi-literal translation using the new "with" statement.
with Shoes():
t = Para("Not clicked!")
with Button("The Label"):
Alert("You clicked the button!")
t.replace("Clicked!")
The hardest part is dealing with the fact that python will not give us anonymous functions with more than one statement in them. To get around that, we could create a list of commands and run through those...
Anyway, here's the backend code I ran this with:
context = None
class Nestable(object):
def __init__(self,caption=None):
self.caption = caption
self.things = []
global context
if context:
context.add(self)
def __enter__(self):
global context
self.parent = context
context = self
def __exit__(self, type, value, traceback):
global context
context = self.parent
def add(self,thing):
self.things.append(thing)
print "Adding a %s to %s" % (thing,self)
def __str__(self):
return "%s(%s)" % (self.__class__.__name__, self.caption)
class Shoes(Nestable):
pass
class Button(Nestable):
pass
class Alert(Nestable):
pass
class Para(Nestable):
def replace(self,caption):
Command(self,"replace",caption)
class Command(Nestable):
def __init__(self, target, command, caption):
self.command = command
self.target = target
Nestable.__init__(self,caption)
def __str__(self):
return "Command(%s text of %s with \"%s\")" % (self.command, self.target, self.caption)
def execute(self):
self.target.caption = self.caption
## All you need is this class:
class MainWindow(Window):
my_button = Button('Click Me')
my_paragraph = Text('This is the text you wish to place')
my_alert = AlertBox('What what what!!!')
#my_button.clicked
def my_button_clicked(self, button, event):
self.my_paragraph.text.append('And now you clicked on it, the button that is.')
#my_paragraph.text.changed
def my_paragraph_text_changed(self, text, event):
self.button.text = 'No more clicks!'
#my_button.text.changed
def my_button_text_changed(self, text, event):
self.my_alert.show()
## The Style class is automatically gnerated by the framework
## but you can override it by defining it in the class:
##
## class MainWindow(Window):
## class Style:
## my_blah = {'style-info': 'value'}
##
## or like you see below:
class Style:
my_button = {
'background-color': '#ccc',
'font-size': '14px'}
my_paragraph = {
'background-color': '#fff',
'color': '#000',
'font-size': '14px',
'border': '1px solid black',
'border-radius': '3px'}
MainWindow.Style = Style
## The layout class is automatically generated
## by the framework but you can override it by defining it
## in the class, same as the Style class above, or by
## defining it like this:
class MainLayout(Layout):
def __init__(self, style):
# It takes the custom or automatically generated style class upon instantiation
style.window.pack(HBox().pack(style.my_paragraph, style.my_button))
MainWindow.Layout = MainLayout
if __name__ == '__main__':
run(App(main=MainWindow))
It would be relatively easy to do in python with a bit of that metaclass python magic know how. Which I have. And a knowledge of PyGTK. Which I also have. Gets ideas?
With some Metaclass magic to keep the ordering I have the following working. I'm not sure how pythonic it is but it is good fun for creating simple things.
class w(Wndw):
title='Hello World'
class txt(Txt): # either a new class
text='Insert name here'
lbl=Lbl(text='Hello') # or an instance
class greet(Bbt):
text='Greet'
def click(self): #on_click method
self.frame.lbl.text='Hello %s.'%self.frame.txt.text
app=w()
The only attempt to do this that I know of is Hans Nowak's Wax (which is unfortunately dead).
The closest you can get to rubyish blocks is the with statement from pep343:
http://www.python.org/dev/peps/pep-0343/
If you use PyGTK with glade and this glade wrapper, then PyGTK actually becomes somewhat pythonic. A little at least.
Basically, you create the GUI layout in Glade. You also specify event callbacks in glade. Then you write a class for your window like this:
class MyWindow(GladeWrapper):
GladeWrapper.__init__(self, "my_glade_file.xml", "mainWindow")
self.GtkWindow.show()
def button_click_event (self, *args):
self.button1.set_label("CLICKED")
Here, I'm assuming that I have a GTK Button somewhere called button1 and that I specified button_click_event as the clicked callback. The glade wrapper takes a lot of effort out of event mapping.
If I were to design a Pythonic GUI library, I would support something similar, to aid rapid development. The only difference is that I would ensure that the widgets have a more pythonic interface too. The current PyGTK classes seem very C to me, except that I use foo.bar(...) instead of bar(foo, ...) though I'm not sure exactly what I'd do differently. Probably allow for a Django models style declarative means of specifying widgets and events in code and allowing you to access data though iterators (where it makes sense, eg widget lists perhaps), though I haven't really thought about it.
Maybe not as slick as the Ruby version, but how about something like this:
from Boots import App, Para, Button, alert
def Shoeless(App):
t = Para(text = 'Not Clicked')
b = Button(label = 'The label')
def on_b_clicked(self):
alert('You clicked the button!')
self.t.text = 'Clicked!'
Like Justin said, to implement this you would need to use a custom metaclass on class App, and a bunch of properties on Para and Button. This actually wouldn't be too hard.
The problem you run into next is: how do you keep track of the order that things appear in the class definition? In Python 2.x, there is no way to know if t should be above b or the other way around, since you receive the contents of the class definition as a python dict.
However, in Python 3.0 metaclasses are being changed in a couple of (minor) ways. One of them is the __prepare__ method, which allows you to supply your own custom dictionary-like object to be used instead -- this means you'll be able to track the order in which items are defined, and position them accordingly in the window.
This could be an oversimplification, i don't think it would be a good idea to try to make a general purpose ui library this way. On the other hand you could use this approach (metaclasses and friends) to simplify the definition of certain classes of user interfaces for an existing ui library and depending of the application that could actually save you a significant amount of time and code lines.
I have this same problem. I wan to to create a wrapper around any GUI toolkit for Python that is easy to use, and inspired by Shoes, but needs to be a OOP approach (against ruby blocks).
More information in: http://wiki.alcidesfonseca.com/blog/python-universal-gui-revisited
Anyone's welcome to join the project.
If you really want to code UI, you could try to get something similar to django's ORM; sth like this to get a simple help browser:
class MyWindow(Window):
class VBox:
entry = Entry()
bigtext = TextView()
def on_entry_accepted(text):
bigtext.value = eval(text).__doc__
The idea would be to interpret some containers (like windows) as simple classes, some containers (like tables, v/hboxes) recognized by object names, and simple widgets as objects.
I dont think one would have to name all containers inside a window, so some shortcuts (like old-style classes being recognized as widgets by names) would be desirable.
About the order of elements: in MyWindow above you don't have to track this (window is conceptually a one-slot container). In other containers you can try to keep track of the order assuming that each widget constructor have access to some global widget list. This is how it is done in django (AFAIK).
Few hacks here, few tweaks there... There are still few things to think of, but I believe it is possible... and usable, as long as you don't build complicated UIs.
However I am pretty happy with PyGTK+Glade. UI is just kind of data for me and it should be treated as data. There's just too much parameters to tweak (like spacing in different places) and it is better to manage that using a GUI tool. Therefore I build my UI in glade, save as xml and parse using gtk.glade.XML().
Personally, I would try to implement JQuery like API in a GUI framework.
class MyWindow(Window):
contents = (
para('Hello World!'),
button('Click Me', id='ok'),
para('Epilog'),
)
def __init__(self):
self['#ok'].click(self.message)
self['para'].hover(self.blend_in, self.blend_out)
def message(self):
print 'You clicked!'
def blend_in(self, object):
object.background = '#333333'
def blend_out(self, object):
object.background = 'WindowBackground'
Here's an approach that goes about GUI definitions a bit differently using class-based meta-programming rather than inheritance.
This is largley Django/SQLAlchemy inspired in that it is heavily based on meta-programming and separates your GUI code from your "code code". I also think it should make heavy use of layout managers like Java does because when you're dropping code, no one wants to constantly tweak pixel alignment. I also think it would be cool if we could have CSS-like properties.
Here is a rough brainstormed example that will show a column with a label on top, then a text box, then a button to click on the bottom which shows a message.
from happygui.controls import *
MAIN_WINDOW = Window(width="500px", height="350px",
my_layout=ColumnLayout(padding="10px",
my_label=Label(text="What's your name kiddo?", bold=True, align="center"),
my_edit=EditBox(placeholder=""),
my_btn=Button(text="CLICK ME!", on_click=Handler('module.file.btn_clicked')),
),
)
MAIN_WINDOW.show()
def btn_clicked(sender): # could easily be in a handlers.py file
name = MAIN_WINDOW.my_layout.my_edit.text
# same thing: name = sender.parent.my_edit.text
# best practice, immune to structure change: MAIN_WINDOW.find('my_edit').text
MessageBox("Your name is '%s'" % ()).show(modal=True)
One cool thing to notice is the way you can reference the input of my_edit by saying MAIN_WINDOW.my_layout.my_edit.text. In the declaration for the window, I think it's important to be able to arbitrarily name controls in the function kwargs.
Here is the same app only using absolute positioning (the controls will appear in different places because we're not using a fancy layout manager):
from happygui.controls import *
MAIN_WINDOW = Window(width="500px", height="350px",
my_label=Label(text="What's your name kiddo?", bold=True, align="center", x="10px", y="10px", width="300px", height="100px"),
my_edit=EditBox(placeholder="", x="10px", y="110px", width="300px", height="100px"),
my_btn=Button(text="CLICK ME!", on_click=Handler('module.file.btn_clicked'), x="10px", y="210px", width="300px", height="100px"),
)
MAIN_WINDOW.show()
def btn_clicked(sender): # could easily be in a handlers.py file
name = MAIN_WINDOW.my_edit.text
# same thing: name = sender.parent.my_edit.text
# best practice, immune to structure change: MAIN_WINDOW.find('my_edit').text
MessageBox("Your name is '%s'" % ()).show(modal=True)
I'm not entirely sure yet if this is a super great approach, but I definitely think it's on the right path. I don't have time to explore this idea more, but if someone took this up as a project, I would love them.
Declarative is not necessarily more (or less) pythonic than functional IMHO. I think a layered approach would be the best (from buttom up):
A native layer that accepts and returns python data types.
A functional dynamic layer.
One or more declarative/object-oriented layers.
Similar to Elixir + SQLAlchemy.

Categories

Resources