Is there a way in PyQt, for QAbstractScrollArea to get the current scroll location?
I'd like to save the current scroll location so that next time I reload the entire window (with new data), I'd automatically scroll to the "saved" scroll location.
Here is my full code.
First, I have a QTableView that inherits from QAbstractScrollArea
tableView = QtWidgets.QTableView()
I load it with data, and then draw the GUI.
tableView.setModel(some_model)
So far, so good.
But eventually, I refresh the entire GUI with new data.
def draw():
tableView.setModel(refreshed_model)
If this is all I do, when I refresh the view, it automatically just display the view without any scroll, essentially bringing my view back to the beginning.
I was hoping for something like the following
def draw():
savedPos = tableView.verticalScrollBar().getCurrentPosition() # doesn't exist
tableView.setModel(refreshed_model)
tableView.verticalScrollBar().setCurrentPosition(savedPos)
Meaning if I was scrolled to the bottom right before the refresh, and then I refresh, I'd actually automatically scroll to the bottom. Essentially "saving" my place
Related
I have been working on a app that allows user to fill details on a form but while making the form in design view one problem has slowed me down so much.
i am using a scroll view so every new item gets added at the bottom so its always off screen.
i scrolls down to see how it looks but after i do anything the scroll view auto scrolls to the top so i have to scroll down again and again.
is there a way to stop auto scrolling to top when code get updated in the editor.
I created a QScrollArea to show a directory tree and a file tree. When directories or files are shown in this area, the vertical scrollbar appears, but the horizontal scrollbar never works. This is the code (the actual code is very large, so I'm showing only relevant portions):
class SomeWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
def DirectoryOrFileSelection(self):
layoutOne = QVBoxLayout()
self.treeview_tabs = QTabWidget()
self.directoryView = QScrollArea(widgetResizable=True) #QWidget()
self.directoryView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.filesView = QScrollArea(widgetResizable=True) #QWidget()
self.filesView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.treeview_tabs.addTab(self.directoryView, "Dirs")
self.treeview_tabs.addTab(self.filesView, "Files")
theHBoxLayout = QHBoxLayout()
leftSpacing = 4
theHBoxLayout.addWidget(self.treeview_tabs, leftSpacing)
layoutOne.addLayout(theHBoxLayout)
self.layout.addLayout(layoutOne)
This is what it looks like when there's no need for a scrollbar.
The vertical scrollbar promptly appears when I make the main window smaller.
However, when I expand one of the directories, even though the filenames go beyond the visible area, the horizontal scrollbar does not appear.
If I use Qt.ScrollBarAlwaysOn in place of Qt.ScrollBarAsNeeded, an inactive scrollbar shows up and never becomes active.
Could anyone help with how to make the horizontal scrollbar active when necessary? I need to be able to scroll horizontally to see the full filenames.
UPDATE: As per suggestions received, I applied the setHorizontalScrollBarPolicy directly to the QTreeView, but even though the scrollbar appears and looks like it's active, it does not seem to recognize when the content is going outside the view area. Shown in the image below.
There is no need to add the view to another scroll area, as all Qt item views are scroll areas.
Note that, unlike the headers of QTableView, the header of QTreeView automatically stretches the last section:
Note: The horizontal headers provided by QTreeView are configured with this property set to true, ensuring that the view does not waste any of the space assigned to it for its header. If this value is set to true, this property will override the resize mode set on the last section in the header.
This means that if you are only showing the first column of the tree, the names of files and directories will always be elided if their width exceeds the width of the widget, and the horizontal scroll bar will not be activated even if it's always shown.
treeview.header().setStretchLastSection(False)
treeview.header().setSectionResizeMode(QHeaderView.ResizeToContents)
I'm implementing a login screen. It has a centred Column containing a TextField for entering an email address and a number of buttons for social sign-in. When a button is pressed, the Column is rebuilt with a CircularProgressIndicator (spinner) added at the bottom. I've wrapped the spinner in a custom widget that manages a ScaleTransition animation. That works OK, but the Column grows and shifts up to accommodate the final full size of the spinner immediately instead of moving smoothly with the animation.
I also wrapped the Column in an AnimatedSize widget using the same curve and duration as the ScaleTransition, but it hasn't helped. The root of the tree is a Consumer of the state that triggers the addition of the spinner so the animations should both be starting together. Maybe the the spinner needs to be permanent for this to work, but it doesn't seem desirable because its API doesn't provide a way to pause the animation. It would presumably constantly use some power to animate itself even when it isn't visible. I'm fussy about that sort of thing.
It looks like I might have to use an AnimatedList, but it doesn't seem semantically correct because the contents aren't really a list. So is there a more generic way to animate the size (and position) of parent widgets like Column and Center to track an animated size change of their children?
I created a QScrollArea to show a directory tree and a file tree. When directories or files are shown in this area, the vertical scrollbar appears, but the horizontal scrollbar never works. This is the code (the actual code is very large, so I'm showing only relevant portions):
class SomeWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
def DirectoryOrFileSelection(self):
layoutOne = QVBoxLayout()
self.treeview_tabs = QTabWidget()
self.directoryView = QScrollArea(widgetResizable=True) #QWidget()
self.directoryView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.filesView = QScrollArea(widgetResizable=True) #QWidget()
self.filesView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.treeview_tabs.addTab(self.directoryView, "Dirs")
self.treeview_tabs.addTab(self.filesView, "Files")
theHBoxLayout = QHBoxLayout()
leftSpacing = 4
theHBoxLayout.addWidget(self.treeview_tabs, leftSpacing)
layoutOne.addLayout(theHBoxLayout)
self.layout.addLayout(layoutOne)
This is what it looks like when there's no need for a scrollbar.
The vertical scrollbar promptly appears when I make the main window smaller.
However, when I expand one of the directories, even though the filenames go beyond the visible area, the horizontal scrollbar does not appear.
If I use Qt.ScrollBarAlwaysOn in place of Qt.ScrollBarAsNeeded, an inactive scrollbar shows up and never becomes active.
Could anyone help with how to make the horizontal scrollbar active when necessary? I need to be able to scroll horizontally to see the full filenames.
UPDATE: As per suggestions received, I applied the setHorizontalScrollBarPolicy directly to the QTreeView, but even though the scrollbar appears and looks like it's active, it does not seem to recognize when the content is going outside the view area. Shown in the image below.
There is no need to add the view to another scroll area, as all Qt item views are scroll areas.
Note that, unlike the headers of QTableView, the header of QTreeView automatically stretches the last section:
Note: The horizontal headers provided by QTreeView are configured with this property set to true, ensuring that the view does not waste any of the space assigned to it for its header. If this value is set to true, this property will override the resize mode set on the last section in the header.
This means that if you are only showing the first column of the tree, the names of files and directories will always be elided if their width exceeds the width of the widget, and the horizontal scroll bar will not be activated even if it's always shown.
treeview.header().setStretchLastSection(False)
treeview.header().setSectionResizeMode(QHeaderView.ResizeToContents)
Language: Progress 10.1C
I have a Windows form, and on it I dynamically create a number of widgets (toggle-boxes in this case). I can create anything from 0 to 64 widgets, depending on how many do-hickies the user has in the current collection. As the user changes from one collection to another, the widgets are deleted or created as needed. The form will resize according to how many widgets are displayed.
The user can check any number of them and perform certain processes on the checked items by selecting actions from a menu bar, toolbar or keyboard shortcuts. But I also want the user to be able to right-click on a single widget, which should bring up a popup menu with actions that can be performed on just the one clicked item, whether it's checked or not.
In itself, this is pretty straight forward. In the past I had done this kind of thing (in other languages) by having a single popup menu that pops up when the user clicks on any one of the dynamic controls. But I'm having a hard time doing this simple thing in Progress:
I am unable to have one popup menu that responds to the right-click of all the widgets. I tried creating a single popup menu, and then as I create each dynamic widget, I set its popup-menu attribute to this menu. The problem is that a menu can only be applied to one widget. Once I've assigned it to Widget1, I cannot assign it to Widget2. This leads one to think of creating a seperate but identical menu for each widget. And as the widgets are destroyed and recreated, so will these menus. In a single session I will create and destroy hundreds or even thousands of these identical menus, while the user might use one of them once or twice. Or not at all. So this does not seem like a good option to me.
My next solution would be to create a single menu which I can pop up programatically, but all attempts to programatically pop up a menu have failed. I have tried APPLY "MENU-DROP" TO MENU MyMenu and other similar things and I cannot for the life of me figure out how to do it. I also find it very difficult to search for information on this. It's as if nobody else has ever tried this, and the rare cases where someone has asked about it, there has been no satisfactory answers. I'm suspecting this cannot be done.
While trying all these things, I have also had endless problems with the events MOUSE-MENU-DOWN, MOUSE-MENU-UP and MOUSE-MENU-CLICK. MOUSE-MENU-CLICK seems to never happen, not for my dynamic widgets anyway. I am unable to figure out why. MOUSE-MENU-UP occurs sometimes, but it depends on what happens in MOUSE-MENU-DOWN and other events of the widgets as well as the widgets' parents. I have not been able to figure out exactly when it will or won't fire, it's very counter-intuitive. MOUSE-MENU-DOWN is the only one I can count on firing.
So: I can't show a menu programatically, I can only show it by right clicking on the menu's parent. The only way to avoid having hundreds of identical menus, is to have a popup menu on the parent of the widgets, for example the default frame.
With a static popup menu on the default frame, I can right-click on the widgets and the menu will come up, but it comes up if I click anywhere in the frame. I can disable the menu, and then in the right-click event of the widget, I enable it. This works very well the first time; if I click anywhere in the frame, nothing happens (the menu is diabled), but if I click on my widget, the menu is enabled and pops up. Yay! But now the menu is enabled and now it pops up if I right-click anywhere, on buttons, empty space, progress bars, etc. When do I disable it again? After the menu has popped up, the user can click anywhere and the menu will dissappear. There is no event that fires when the menu is closed, so I'm stuck.
Sorry about the long ramblings, I'll restate the question briefly: I want to have one popup menu that pops up when the user right-clicks one of a number of dynamically created widgets.
Using Tom's answer, this is how I implemented it:
/* Somewhere in Control Definitions... */
DEFINE MENU m_Popup
MENU-ITEM m_Test1 LABEL "Test 1"
MENU-ITEM m_Test2 LABEL "Test 2".
/* Somewhere, where I need to dynamically create the widgets. */
/* Loop through the items in the temp table and create a widget for each. */
FOR EACH ttItem BY ttItem.ItemName:
CREATE TOGGLE-BOX hWidget
ASSIGN
FRAME = FRAME DEFAULT-FRAME:HANDLE
LABEL = STRING(ttItem.ttItemName)
TRIGGERS:
ON MOUSE-MENU-DOWN PERSISTENT RUN GetMenu IN THIS-PROCEDURE.
END TRIGGERS.
END.
/* If the user right-clicks on any one of the widgets, this procedure */
/* is run with SELF being the widget that was clicked on. */
PROCEDURE GetMenu:
/* Remove the menu from its current owner and assign it to SELF. */
MENU m_Popup:OWNER:POPUP-MENU = ?.
SELF:POPUP-MENU = MENU m_Popup:HANDLE.
END PROCEDURE.
/* The user clicks on one of the menu items */
/* Here SELF is the menu item that was clicked. I can */
/* get m_Popup from SELF:PARENT and the widget it was */
/* was assigned to from SELF:PARENT:OWNER. */
ON CHOOSE OF MENU-ITEM m_Test1
DO:
MESSAGE "You selected " SELF:LABEL " for " SELF:PARENT:OWNER:LABEL.
END.
My buddy Mike Fechner (who is not currently on SO but who does this stuff a lot more than I do) tells me that, while this example is static, something very similar should work:
Procedure getMenu:
DEFINE INPUT PARAMETER phWidget AS HANDLE NO-UNDO.
DO WITH FRAME {&FRAME-NAME}:
FILL-IN-1:POPUP-MENU = ? .
FILL-IN-2:POPUP-MENU = ? .
FILL-IN-3:POPUP-MENU = ? .
END.
phWidget:POPUP-MENU = MENU POPUP-MENU-FILL-IN-1:HANDLE .
END procedure.
ON RIGHT-MOUSE-DOWN of all three fill-in’s you "RUN getMenu (SELF)." to steal the popup menu from whomever has it.