Implement Resize option to Qt Frameless widget - c++

How can i implement resize option to Qt frameless widget that it's used as Main Window?

I just encountered this problem as well, and I solved it by adding custom mouseEvent handlers for my QMainWindow. I'm using PyQt, but it should be fairly similar in C++.
In my implementation, dragging the right mouse button anywhere on the frameless widget (called MyClass) resizes it.
When the right mouse button is pressed, store the coordinates:
def mousePressEvent(self, event):
super(MyClass, self).mousePressEvent(event)
if event.button() == QtCore.Qt.RightButton:
self.rdragx = event.x()
self.rdragy = event.y()
self.currentx = self.width()
self.currenty = self.height()
self.rightClick = True
If the mouse is moved while the button is still pressed (i.e., when it's dragged), resize the QMainWindow. Don't allow it to become smaller than the predefined minimum size.
def mouseMoveEvent(self, event):
super(Myclass, self).mouseMoveEvent(event)
if self.rightClick == True:
x = max(frame.minimumWidth(),
self.currentx + event.x() - self.rdragx)
y = max(frame.minimumHeight(),
self.currenty + event.y() - self.rdragy)
self.resize(x, y)
When the mouse button is released, reset the button variable to False to stop resizing on movement.
def mouseReleaseEvent(self, event):
super(MyClass, self).mouseReleaseEvent(event)
self.rightClick = False

Use a QSizeGrip
The QSizeGrip class provides a resize handle for resizing top-level windows.

Related

How to enable autoscroll in Qtableview like in Qtextedit?

I'm using QtableView and QStandardItemModel to display logs on GUI to maintain proper spacing and filter logs. I created model and inserted data into it. Used QSortFilterProxyModel for filter strings.
self.tableView = QtGui.QTableView(self)
self.model = QtGui.QStandardItemModel(self)
self.proxy = QtGui.QSortFilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.tableView.setModel(self.proxy)
In a sec, nearly 100 logs are expected and should be shown on GUI. When new logs are appended, the view isn't auto scrolling and the slider stays only at the top. It doesn't give live feel for logging and user need to scroll manually to the end. So to overcome this, i used following syntax,
self.model.rowsInserted.connect(lambda: QtCore.QTimer.singleShot(5, self.tableView.scrollToBottom))
It gives live feel for logs, but the slider remains always in bottom and i'm not able to scroll up to see previous logs. Whenever i try to move the slider, it immediately comes down to bottom again. So this syntax doesn't meet my requirement. In QTextEdit, auto scrolling is proper and user friendly. I want the same scenario here on QtableView. Is there any alternative for auto scrolling which resembles like QTextEdit ?
To get the required behaviour, you can auto-scroll only when the previous scroll position is at the bottom. That way, whenever the user scrolls away from the bottom, auto-scrolling will be disabled; but when they scroll back to the bottom, auto-scrolling will be re-enabled. (NB: to quickly re-enable auto-scroll, right-click the scrollbar and select "Bottom" from the context menu).
Here is a simple demo:
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.table = QtGui.QTableView(self)
self.model = QtGui.QStandardItemModel(self)
self.table.setModel(self.model)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.table)
self._scroll = True
self.model.rowsAboutToBeInserted.connect(self.beforeInsert)
self.model.rowsInserted.connect(self.afterInsert)
def beforeInsert(self):
vbar = self.table.verticalScrollBar()
self._scroll = vbar.value() == vbar.maximum()
def afterInsert(self):
if self._scroll:
self.table.scrollToBottom()
def addRow(self):
self.model.appendRow([QtGui.QStandardItem(c) for c in 'ABC'])
if __name__ == '__main__':
app = QtGui.QApplication([''])
window = Window()
window.setGeometry(500, 50, 400, 300)
window.show()
timer = QtCore.QTimer()
timer.timeout.connect(window.addRow)
timer.start(200)
app.exec_()

Panel size does not expand in a frame with a menu

I've created a frame with a menu bar in wxpython and set the main sizer as a child of the frame itself, but the frame's panel stays in a fixed size after resizing the frame. When removing the menu bar and setting the main sizer to the panel itself resizing works perfectly, but with the menu added to the frame setting the main sizer to the panel doesn't work.
tl;dr - the panel size for this code stays fixed. How do I make it resizable?
import wx
class Mainframe(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self,parent,title=title,size=(650,450))
self.Main_Panel = wx.Panel(self,id=wx.ID_ANY)
#################################################################
### Template menubar menubar
#################################################################
Menubar = wx.MenuBar()
Filemenu = wx.Menu()
Debugmenu = wx.Menu()
self.Menu_Open_File = Filemenu.Append(wx.ID_OPEN,"Open","Open an nrix file to edit")
self.Menu_SaveAs_File = Filemenu.Append(wx.ID_SAVEAS,"Save as","Normal saving will be incorporated later")
Filemenu.AppendSeparator()
self.Menu_About_Dialog = Filemenu.Append(wx.ID_ABOUT,"About","Information about the program and changelog")
self.Debug_State_Switch = Debugmenu.Append(wx.ID_ANY,"Debug on","Turn on debug mode",wx.ITEM_CHECK)
Menubar.Append(Filemenu,"File")
Menubar.Append(Debugmenu,"Debug")
self.SetMenuBar(Menubar)
#################################################################
### Frame objects
#################################################################
Left_Static_text = wx.StaticText(self.Main_Panel,wx.ID_ANY,style=wx.ALIGN_LEFT)
Left_Static_text.SetFont(wx.Font(16,wx.ROMAN,wx.NORMAL,wx.FONTWEIGHT_NORMAL))
Left_Static_text.SetLabel("Exploitable text")
Left_Text_Control = wx.TextCtrl(self.Main_Panel,wx.ID_ANY,value="Exploitable")
Buttons = []
for i in range(4):
Buttons.append(wx.Button(self.Main_Panel,label=str(i + 1),size=(50,50)))
#################################################################
### Frame sizers
#################################################################
Grid_Sizer = wx.GridSizer(2,2,15,15)
Ver_Wrapper = wx.BoxSizer(wx.VERTICAL)
Left_Sizer = wx.BoxSizer(wx.VERTICAL)
Whole_Sizer = wx.BoxSizer(wx.HORIZONTAL)
#################################################################
### Attaching sizers
#################################################################
for i in range(4):
Grid_Sizer.Add(Buttons[i],0,wx.EXPAND)
Ver_Wrapper.Add((0,0),proportion=1,flag=wx.EXPAND)
Ver_Wrapper.Add(Grid_Sizer,proportion=0,flag=wx.CENTER)
Ver_Wrapper.Add((0,0),proportion=1,flag=wx.EXPAND)
Left_Sizer.Add((0,0),proportion=1,flag=wx.EXPAND)
Left_Sizer.Add(Left_Static_text,proportion=0,flag=wx.CENTER)
Left_Sizer.Add(Left_Text_Control,proportion=0,flag=wx.CENTER)
Left_Sizer.Add((0,0),proportion=1,flag=wx.EXPAND)
Whole_Sizer.Add(Left_Sizer,proportion=1,flag=wx.EXPAND)
Whole_Sizer.Add(Ver_Wrapper,proportion=3,flag=wx.EXPAND)
# self.Main_Panel.SetSizer(Whole_Sizer) <- THIS WORKS WITHOUT A MENU
self.SetSizer(Whole_Sizer)
app = wx.App(False)
frame = Mainframe(None,"Menu UI")
frame.Show(True)
app.MainLoop()
Turns out this is just a small glitch. When Whole_Sizer is attributed to Main_Panel in Windows, all of the widgets are positioned in the upper left corner. Once I started messing with the window size by dragging the window corner around the whole thing solved itself. Now I just feel stupid.

How can I determine which child windows will be visible in a ScrolledWindow during/after a EVT_SCROLLED_WIN event?

I'm building a panel for my application which contains a large number of thumbnail images; they won't all fit in the window, so I'm using a ScrolledWindow to hold them and allow the user to scroll to view them.
It takes some time to generate the thumbnail images, so I'm attempting to prioritize the production of the images for the thumbnails that are currently visible in the window. I'm also wanting to capture which thumbnails are visible so that if I add or remove a subset of the thumbnails I know where to position the scrollbar to include as many of the previously visible ones as possible.
The problem I'm having is that EVT_SCROLLWIN appears to be posted before the child windows are repositioned in the client area of the ScrolledWindow; in other words, I'm measuring the visible objects from before the scrollbar moved. Is there a way to: a) force the ScrolledWindow to update the child window positions, b) reliably determine what the new client window offset will be as compared to what it is now, or c) call the event handler after this update has occurred?
If it matters, I'm running Python 2.7.11 and wxPython 3.0.2.0 on this computer.
A code segment follows:
class ThumbsViewer(wx.ScrolledWindow):
def __init__(self, parent, info, style = wx.BORDER_RAISED | wx.HSCROLL):
wx.ScrolledWindow.__init__(self, parent = parent, id = wx.ID_ANY, size = wx.DefaultSize,
pos = wx.DefaultPosition, style = style, name = "ThumbsViewer")
self.__info__ = info
self.__bitmapctrls__ = []
self.__selected__ = []
self.__lastclicked__ = []
self.Bind(wx.EVT_SCROLLWIN, self.__OnScroll__)
# ... Code to generate placeholder thumbnails, arrange buttons, size window, etc. ...
# ... Various functions to handle clicking on thumbnails, etc. ...
# EVT_SCROLLWIN Handler - Measure client window positions and determine which are
# visible on the screen.
def __OnScroll__(self, event):
if event:
event.Skip()
priority = []
clientsize = self.GetClientSize()
for ctrl in self.__bitmapctrls__:
pos = ctrl.GetPosition()
size = ctrl.GetSize()
# Test to see if any part of the thumbnail control falls within the client
# area...append to the priority list if it does.
# This appears to be where the problem is - the child window positions
# have not been updated to reflect the new scrollbar position yet.
if (((pos[0] >= 0 and pos[0] <= clientsize[0]) or (pos[0] + size[0] >= 0 and pos[0] + size[0] <= clientsize[0]))
and ((pos[1] >= 0 and pos[1] <= clientsize[1]) or (pos[1] + size[1] >= 0 and pos[1] + size[1] <= clientsize[1]))):
priority.append(ctrl.GetPage())
self.__info__.SetPriorityPages(priority)
After struggling with this for a while, I decided to Bind EVT_PAINT instead of EVT_SCROLLWIN, and do my scroll bar checks when that occurs. The repaint happens after EVT_SCROLLWIN and the child window positions are updated.
I'm not super happy with this solution, because EVT_PAINT happens for a lot of reasons having nothing to do with the scroll bars, so please feel free to point out a better solution if you have one. Fortunately, in this case the computations fall through very quickly except when I'm adding or removing thumbnails (which doesn't occur on most paint events) so the performance hit is minimal.

QLineEdit hover-over Signal - when mouse is over the QlineEdit

I have a QLineEdit, and I need to know if there is a signal which can track mouse hover over that QLineEdit, and once mouse is over that QLineEdit it emits a signal.
I have seen the documents, and found we have the following signals:
cursorPositionChanged ( int old, int new )
editingFinished ()
returnPressed ()
selectionChanged ()
textChanged ( const QString & text )
textEdited ( const QString & text )
However, none of this is exactly for hover-over. Can you suggest if this can be done by any other way in PyQt4?
There is no built-in mouse-hover signal for a QLineEdit.
However, it is quite easy to achieve something similar by installing an event-filter. This technique will work for any type of widget, and the only other thing you might need to do is to set mouse tracking (although this seems to be switched on by default for QLineEdit).
The demo script below show how to track various mouse movement events:
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.edit = QtGui.QLineEdit(self)
self.edit.installEventFilter(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.edit)
def eventFilter(self, source, event):
if source is self.edit:
if event.type() == QtCore.QEvent.MouseMove:
pos = event.globalPos()
print('pos: %d, %d' % (pos.x(), pos.y()))
elif event.type() == QtCore.QEvent.Enter:
print('ENTER')
elif event.type() == QtCore.QEvent.Leave:
print('LEAVE')
return QtGui.QWidget.eventFilter(self, source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 100)
window.show()
sys.exit(app.exec_())
You can use enterEvent, leaveEvent, enterEvent is triggered when mouse enters the widget and leave event is triggered when the mouse leaves the widget. These events are in the QWidget class, QLineEdit inherits QWidget, so you can use these events in QLineEdit. I you don't see these events in QLineEdit's documentation, click on the link List of all members, including inherited members at the top of the page.

How to make a QPushButton pressable for enter key?

I want to make my app laptop friendly. I can tab to everywhere, but if I tab to a QPushButton I can't press it with Enter, only with space.
What's the problem? How to make it pressable for Enter?
tl;dr
In the Qt Creator's UI view, select the button you want to make pressable for Enter.
In the right side at the Property Editor scroll down to the blue part titled QPushButton.
Check the checkbox by autoDefault or default.
Most of the cases the main different between autoDefault and default is how the button will be rendered. But there are cases where it can cause unexpected things so for more information read more below.
Full review
Overview
Every QPushButton has 3 properties that are not inherited. From these, two (default and autoDefault) have a major role when we place buttons on QDialogs, because these settings (and the focus on one of the buttons) decides which button will be pressed if we hit Enter.
All of these properties are set false by default. Only expection is autoDefault that will be true if the button has a QDialog parent.
Every time you press space the button with the focus on it will be pressed. The followings will describe what happens if you press Enter.
Default property
If this is set true, the button will be a default button.
If Enter is pressed on the dialog, than this button will be pressed, except when the focus is on an autoDefault button.
There should be only one default button. If you add more then the last one added will be the default button.
AutoDefault property
If this is set true, the button will be an autoDefault button.
If Enter is pressed on the dialog, than this button will be pressed if the focus is on it.
If the focus is not on an autoDefault button and there is no default button than the next autoDefault button will be pressed for Enter.
Flat property
If this is set true, then the border of the button will not be raised.
Example tables
The following tables show which button will be pressed with different buttons on different focus. The buttons are added from left to right.
Test code
The following code is a way to add buttons to a dialog. It can be used for testing by changing the boolean values at setDefault() and/or setAutoDefault().
You just need to create a window, add a QPushButton called pushButton and a QLabel called label (that is the default naming). Don't forget to #include <QMessageBox>. Then copy this code to the button's clicked() signal:
void MainWindow::on_pushButton_clicked()
{
QMessageBox msgBox;
QPushButton button("Button");
button.setDefault(false);
button.setAutoDefault(false);
msgBox.addButton(&button, QMessageBox::ActionRole);
QPushButton autodefaultbutton("AutoDefault Button");
autodefaultbutton.setDefault(false);
autodefaultbutton.setAutoDefault(true);
msgBox.addButton(&autodefaultbutton, QMessageBox::ActionRole);
QPushButton autodefaultbutton2("AutoDefault Button2");
autodefaultbutton2.setDefault(false);
autodefaultbutton2.setAutoDefault(true);
msgBox.addButton(&autodefaultbutton2, QMessageBox::ActionRole);
QPushButton defaultbutton("Default Button");
defaultbutton.setDefault(true);
defaultbutton.setAutoDefault(false);
msgBox.addButton(&defaultbutton, QMessageBox::ActionRole);
msgBox.exec();
if (msgBox.clickedButton() == &button) {
ui->label->setText("Button");
} else if (msgBox.clickedButton() == &defaultbutton) {
ui->label->setText("Default Button");
} else if (msgBox.clickedButton() == &autodefaultbutton) {
ui->label->setText("AutoDefault Button");
} else if (msgBox.clickedButton() == &autodefaultbutton2) {
ui->label->setText("AutoDefault Button2");
}
}
Display
If you compile the code you can get this window. You doesn't even have to click to the buttons because the way they are rendered by the OS shows which one will be pressed if you hit Enter or space.
Official documentation
Most of this answer was made according to the official documentation.
The QPushButton documentation made by Qt states these:
Default and autodefault buttons decide what happens when the user
presses enter in a dialog.
A button with this property set to true (i.e., the dialog's default
button,) will automatically be pressed when the user presses enter,
with one exception: if an autoDefault button currently has focus, the
autoDefault button is pressed. When the dialog has autoDefault buttons
but no default button, pressing enter will press either the
autoDefault button that currently has focus, or if no button has
focus, the next autoDefault button in the focus chain.
In a dialog, only one push button at a time can be the default button.
This button is then displayed with an additional frame (depending on
the GUI style).
The default button behavior is provided only in dialogs. Buttons can
always be clicked from the keyboard by pressing Spacebar when the
button has focus.
If the default property is set to false on the current default button
while the dialog is visible, a new default will automatically be
assigned the next time a pushbutton in the dialog receives focus.
It's also worth to check QDialog and QMessageBox.
According to Qt's documentation Enter should work
Command buttons in dialogs are by default auto-default buttons, i.e. they become the default push button automatically when they receive the keyboard input focus. A default button is a push button that is activated when the user presses the Enter or Return key in a dialog. You can change this with setAutoDefault().
http://qt-project.org/doc/qt-4.8/qpushbutton.html
totymedli's answer is great. I added a small program to test various combinations of isDefault, autoDefault, setDefault and setAutoDefault functions.
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
autoDefaultInitialState = True
defaultInitialState = False
self.lineEdit1 = QLineEdit(self)
self.lineEdit2 = QLineEdit(self)
self.lineEdit3 = QLineEdit(self)
# if we create a new button (e.g. "Print state"), with the same function,
# it doesn't work, because adding a new button (apart from our 3 buttons)
# produces total mess, so we use this lineedit for this purpose
self.lineEdit1.returnPressed.connect(self.printState)
#------------------------------------
self.chkAutoDefaultOk = QCheckBox('OK setAutoDefault', self)
self.chkAutoDefaultCancel = QCheckBox('Cancel setAutoDefault', self)
self.chkAutoDefaultApply = QCheckBox('Apply setAutoDefault', self)
self.chkDefaultOk = QCheckBox('OK setDefault', self)
self.chkDefaultCancel = QCheckBox('Cancel setDefault', self)
self.chkDefaultApply = QCheckBox('Apply setDefault', self)
self.chkAutoDefaultOk.setChecked(autoDefaultInitialState)
self.chkAutoDefaultCancel.setChecked(autoDefaultInitialState)
self.chkAutoDefaultApply.setChecked(autoDefaultInitialState)
self.chkDefaultOk.setChecked(defaultInitialState)
self.chkDefaultCancel.setChecked(defaultInitialState)
self.chkDefaultApply.setChecked(defaultInitialState)
#------------------------------------
self.pushButtonOk = QPushButton(self)
self.pushButtonOk.setText("Ok")
self.pushButtonOk.clicked.connect(lambda : print('ok'))
self.pushButtonCancel = QPushButton(self)
self.pushButtonCancel.setText("Cancel")
self.pushButtonCancel.clicked.connect(lambda : print('cancel'))
self.pushButtonApply = QPushButton(self)
self.pushButtonApply.setText("Apply")
self.pushButtonApply.clicked.connect(lambda : print('apply'))
#------------------------------------
self.pushButtonOk.setAutoDefault(autoDefaultInitialState)
self.pushButtonCancel.setAutoDefault(autoDefaultInitialState)
self.pushButtonApply.setAutoDefault(autoDefaultInitialState)
self.pushButtonOk.setDefault(defaultInitialState)
self.pushButtonCancel.setDefault(defaultInitialState)
self.pushButtonApply.setDefault(defaultInitialState)
#------------------------------------
self.chkAutoDefaultOk.stateChanged.connect(self.chkChangeState)
self.chkAutoDefaultCancel.stateChanged.connect(self.chkChangeState)
self.chkAutoDefaultApply.stateChanged.connect(self.chkChangeState)
self.chkDefaultOk.stateChanged.connect(self.chkChangeState)
self.chkDefaultCancel.stateChanged.connect(self.chkChangeState)
self.chkDefaultApply.stateChanged.connect(self.chkChangeState)
#------------------------------------
self.layout = QGridLayout(self)
self.layout.addWidget(self.lineEdit1, 0, 0, 1, 3)
self.layout.addWidget(self.lineEdit2, 1, 0, 1, 3)
self.layout.addWidget(self.lineEdit3, 2, 0, 1, 3)
self.layout.addWidget(self.chkAutoDefaultOk, 3, 0)
self.layout.addWidget(self.chkAutoDefaultCancel, 3, 1)
self.layout.addWidget(self.chkAutoDefaultApply, 3, 2)
self.layout.addWidget(self.chkDefaultOk, 4, 0)
self.layout.addWidget(self.chkDefaultCancel, 4, 1)
self.layout.addWidget(self.chkDefaultApply, 4, 2)
self.layout.addWidget(self.pushButtonOk, 5, 0)
self.layout.addWidget(self.pushButtonCancel, 5, 1)
self.layout.addWidget(self.pushButtonApply, 5, 2)
def chkChangeState(self):
obj = self.sender()
if (obj == self.chkAutoDefaultOk):
self.pushButtonOk.setAutoDefault(self.chkAutoDefaultOk.isChecked())
elif (obj == self.chkAutoDefaultCancel):
self.pushButtonCancel.setAutoDefault(self.chkAutoDefaultCancel.isChecked())
elif (obj == self.chkAutoDefaultApply):
self.pushButtonApply.setAutoDefault(self.chkAutoDefaultApply.isChecked())
elif (obj == self.chkDefaultOk):
self.pushButtonOk.setDefault(self.chkDefaultOk.isChecked())
elif (obj == self.chkDefaultCancel):
self.pushButtonCancel.setDefault(self.chkDefaultCancel.isChecked())
elif (obj == self.chkDefaultApply):
#print('sender: self.chkDefaultApply')
#print('before: ', self.pushButtonApply.isDefault())
self.pushButtonApply.setDefault(self.chkDefaultApply.isChecked())
#print('after: ', self.pushButtonApply.isDefault())
def printState(self):
print(self.pushButtonOk.autoDefault(), self.pushButtonCancel.autoDefault(), self.pushButtonApply.autoDefault())
print(self.pushButtonOk.isDefault(), self.pushButtonCancel.isDefault(), self.pushButtonApply.isDefault())
print('-' * 50)
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
Set the tab order for your widgets. This will allow usage of return key for clicking. Its in there by default inside Qt.