wxwidgets aui manager problem - c++

i am using auimanager to show frame in a frame , however it keeps showing the second frame in a new window instead in the same window, or any other suggestions on how to do it ?

Your "outer frame" should be a aui.AuiMDIParentFrame, whereas the "inner frame" should be aui.AuiMDIChildFrame. In any case: post some code, then it is easier to advise.
Example code:
import wx
import aui
class MainFrame(aui.AuiMDIParentFrame):
def __init__(self, parent):
aui.AuiMDIParentFrame.__init__(self, parent, -1, title="AuiMDIParentFrame",
size=(640, 480), style=wx.DEFAULT_FRAME_STYLE)
child1 = InnerFrame(self, "child 1")
child1.Show()
child2 = InnerFrame(self, "child 2")
child2.Show()
class InnerFrame(aui.AuiMDIChildFrame):
def __init__(self, parent, label):
aui.AuiMDIChildFrame.__init__(self, parent, -1, title=label)
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MainFrame(None)
frame.CenterOnScreen()
frame.Show()
app.MainLoop()

Related

Qt TabWidget Each tab Title Background Color

This is the original Tabwidget without setting title background color
My customer ask me to do something like this;
Set different background colour for title
All - Yellow
purchase - light blue
POS Sales - light green
Cash Sales - Pink
invoice - light purple
I have try the SetStyleSheet like:
QTabBar {
background-color : Yellow;
}
But all tab Color changed
Somebody know how to setting each QTabBar background color?
These properties can not be set through QSS. To change the style to each tab we must create a custom QTabBar and override its paintEvent method, to be able to change the style of each tab we use the QStyleOptionTab class, but to change the QTabWidget tabbar we need to use the setTabBar method but this is private so you need to create a custom QTabWidget as shown below:
tabwidget.h
#ifndef TABWIDGET_H
#define TABWIDGET_H
#include <QStyleOptionTab>
#include <QStylePainter>
#include <QTabWidget>
class TabBar: public QTabBar
{
public:
TabBar(const QHash<QString, QColor> &colors, QWidget *parent=0):QTabBar(parent){
mColors = colors;
}
protected:
void paintEvent(QPaintEvent */*event*/){
QStylePainter painter(this);
QStyleOptionTab opt;
for(int i = 0;i < count();i++)
{
initStyleOption(&opt,i);
if(mColors.contains(opt.text)){
opt.palette.setColor(QPalette::Button, mColors[opt.text]);
}
painter.drawControl(QStyle::CE_TabBarTabShape, opt);
painter.drawControl(QStyle::CE_TabBarTabLabel,opt);
}
}
private:
QHash<QString, QColor> mColors;
};
class TabWidget : public QTabWidget
{
public:
TabWidget(QWidget *parent=0):QTabWidget(parent){
// text - color
QHash <QString, QColor> dict;
dict["All"] = QColor("yellow");
dict["purchase"] = QColor("#87ceeb");
dict["POS Sales"] = QColor("#90EE90");
dict["Cash Sales"] = QColor("pink");
dict["invoice"] = QColor("#800080");
setTabBar(new TabBar(dict));
}
};
#endif // TABWIDGET_H
And to use it in our QTabWidget in Qt Designer should be promoted for this we right click on the tabwidget and select the menu Promoted Widgets, in my case the previous code is created in the file tabwidget.h so this will be the header file and in the case of Promoted Class Name we use TabWidget, after that we press the buttons Add and Promote obtaining what is shown in the following image:
The final result is shown in the following image:
The complete example can be found in the following link
Python:
from PyQt5 import QtGui, QtWidgets
class TabBar(QtWidgets.QTabBar):
def __init__(self, colors, parent=None):
super(TabBar, self).__init__(parent)
self.mColors = colors
def paintEvent(self, event):
painter = QtWidgets.QStylePainter(self)
opt = QtWidgets.QStyleOptionTab()
for i in range(self.count()):
self.initStyleOption(opt, i)
if opt.text in self.mColors:
opt.palette.setColor(
QtGui.QPalette.Button, self.mColors[opt.text]
)
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, opt)
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabLabel, opt)
class TabWidget(QtWidgets.QTabWidget):
def __init__(self, parent=None):
super(TabWidget, self).__init__(parent)
d = {
"All": QtGui.QColor("yellow"),
"purchase": QtGui.QColor("#87ceeb"),
"POS Sales": QtGui.QColor("#90EE90"),
"Cash Sales": QtGui.QColor("pink"),
"invoice": QtGui.QColor("#800080"),
}
self.setTabBar(TabBar(d))
self.addTab(QtWidgets.QLabel(), "All")
self.addTab(QtWidgets.QLabel(), "purchase")
self.addTab(QtWidgets.QLabel(), "POS Sales")
self.addTab(QtWidgets.QLabel(), "Cash Sales")
self.addTab(QtWidgets.QLabel(), "invoice")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle("fusion")
w = TabWidget()
w.show()
sys.exit(app.exec_())

Dialog's ShowModal() won't send EVT_PAINT

wx version: 2.8.12.1
I'm trying to build a decored Dialog (Background, Buttons, Bitmap Border, etc) but on ShowModal() the Paint event is not issued.
It works with Show() super seeding wx.PopupTransientWindow, but not with Show() or ShowModal() on wx.Dialog
If you run the example, open the Dialog and click either of the two buttons, you'll get on the terminal:
send refresh
Clicked OK/CANCEL
"Paint Event" or "Draw Function" won't be printed.
"send refresh" indicates that a manual Refresh() has been issued; and that should issue an wx.EVT_PAINT.
If OnPaint is binded to wx.EVT_SHOW the functions will be called, the wx.BufferedPaintDC will be correctly set, but it won't change anything visible.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import wx
def GetThemeTools(borderWidth, backgrounColour):
return {
'Pens': {
'DarkRectSolidBorder': wx.Pen( wx.Colour(67, 67, 67), borderWidth),
},
'Brushes': {
'Background': wx.Brush(backgrounColour),
'RectBoxFilling': wx.Brush( wx.Colour(119,120,119) ),
},
'ForegroundColor': wx.Colour(241,241,241),
'BackgroundColor': 'WHITE',
'Fonts': {
'Brief': wx.Font( pointSize=12,
family=wx.FONTFAMILY_SWISS,
style=wx.FONTSTYLE_NORMAL,
weight=wx.FONTWEIGHT_NORMAL,
encoding=wx.FONTENCODING_UTF8
),
},
}
class ConfirmDialog(wx.Dialog):
def __init__(self, parent, text="",
margin=10, borderRadio=10):
wx.Dialog.__init__(self, parent, style=wx.STAY_ON_TOP)
# Get data to show
self.parent = parent
self._margin = margin
self._borderRadio = borderRadio
self._text = text
self._font = None
self.initializeTools()
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_SHOW, self._sendRefresh)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self._setWidgets()
self._layout()
wx.CallAfter(self.Refresh)
def _sendRefresh(self, e):
if self.IsShown():
print("send refresh")
self.Refresh()
def _setWidgets(self):
self.panel = wx.Panel(self)
self.message = wx.StaticText(self.panel, label=self.GetText())
self.message.SetForegroundColour(self.tools['ForegroundColor'])
self.message.SetBackgroundColour(self.tools['BackgroundColor'])
self.cancelBtn = wx.Button(self.panel, id=wx.ID_CANCEL, label="Cancel")
self.okBtn = wx.Button(self.panel, id=wx.ID_OK, label="Ok")
def _layout(self):
self.vSizer = wx.BoxSizer(wx.VERTICAL)
self.buttonsSizer = wx.BoxSizer(wx.HORIZONTAL)
self.buttonsSizer.Add(self.okBtn)
self.buttonsSizer.AddSpacer(20)
self.buttonsSizer.Add(self.cancelBtn)
self.vSizer.Add(self.message, 0, wx.CENTER|wx.BOTTOM, 5)
self.vSizer.Add(self.buttonsSizer, 0, wx.CENTER|wx.TOP, 5)
self.panel.SetSizer(self.vSizer)
self.vSizer.Fit(self.panel)
def SetMargin(self, margin):
self._margin = margin
self.Refresh()
def GetMargin(self):
return self._margin
def SetBorderRadio(self, borderRadio):
self._borderRadio = borderRadio
self.Refresh()
def GetBorderRadio(self):
return self._borderRadio
def SetText(self, text):
self._text = text
self.Refresh()
def GetText(self):
return self._text
def GetFont(self):
if not self._font:
self._font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
return self._font
def initializeTools(self):
self.borderWidth = 6
backColour = self.GetBackgroundColour()
self.tools = GetThemeTools(self.borderWidth, backColour)
self._font = self.tools['Fonts']['Brief']
def OnPaint(self, event):
print("Paint Event")
dc = wx.BufferedPaintDC(self)
self.Draw(dc)
event.Skip()
def Draw(self, dc):
print("Draw Function")
margin = self.GetMargin()
borderRadio = self.GetBorderRadio()
text = self.GetText()
self.Layout()
dc.SetBackground(self.tools['Brushes']['Background'])
dc.SetFont(self.GetFont())
dc.SetTextBackground(self.tools['BackgroundColor'])
dc.Clear()
(H, W) = self.GetSize()
# Draw Border
dc.SetPen(self.tools['Pens']['DarkRectSolidBorder'])
dc.SetBrush(self.tools['Brushes']['RectBoxFilling'])
dc.DrawRoundedRectangle( 0, 0, H, W, borderRadio)
def OnEraseBackground(self, event):
pass
class AppFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Custom Dialog Test")
panel = wx.Panel(self)
frameSizer = wx.BoxSizer(wx.VERTICAL)
panelSizer = wx.BoxSizer(wx.VERTICAL)
btn = wx.Button(panel, label="Show Dialog")
btn.Bind(wx.EVT_BUTTON, self.ShowDlg)
panelSizer.Add(btn, 0, wx.ALL, 20)
panel.SetSizer(panelSizer)
self.SetSizer(frameSizer)
panelSizer.Fit(panel)
self.Layout()
self.Show()
def ShowDlg(self, event):
dia = ConfirmDialog(self, "Custom Dialog\nTest\nThird Line")
if dia.ShowModal() == wx.ID_OK:
print("Clicked OK")
else:
print("Clicked CANCEL")
app = wx.App(False)
frame = AppFrame()
app.MainLoop()
You've bound your paint event handler to the dialog, but the dialog has a panel that completely covers the dialog. Since the dialog's surface is never exposed (because it is under the panel) then it never needs painted, so it never recieves a paint event from the system.
In most cases you do not need a panel as the top element in a dialog, so you may want to remove it and replace all the self.panel in that part of the code with just self.
Following the hint on Drawing To Panel Inside of Frame:
I got wrong the bind of the Paint Event
self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
self.panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
For some reason I still don't fully get (examples at self.Bind vs self.button.Bind) why the panel won't Skip() the Paint Event; leading to self never getting aware of the Paint Event.
And the parenthood of the canvas:
dc = wx.BufferedPaintDC(self.panel)
After playing a little bit subclassing from wx.PopupWindow (which resulted in a badly sized Window that I couldn't properly center in the frame) I realized that the DC was being rendered below the panel. It makes sence since self.panel is a child of self, so only self was being painted.
Not an Error but on the Paint Event, it seems the panel or dialog gets back to their default size. If the panel is fit, the dialog is resized, and the window is centered again in the Paint Event, then new Paint Events will be raised and a flicker will appear. Since this is an parameterized static dialog, the dimensions can be fixed with this at the end of self._layout():
self.panel.Fit()
minSize = self.panel.GetSize()
self.SetMinSize(minSize)
self.SetMaxSize(minSize)

Closing a ApplicationWindow and open a new one?

I'm facing this problem while trying to switch from a ApplicationWindow to another.
I have this first Window called login.qml which opens the main.qml through the following signal:
onIdAutenticadoChanged: {
if(idAutenticado > 0){
console.log("login.qml: Autenticado ID: " + idAutenticado);
LoginController.abrirMain();
close();
} else if(idAutenticado == 0){
senhaInput.text = "";
console.log("Falha na autenticação: Usuário e/ou senha inválidos.");
lblMsgErro.text = "Usuário e/ou senha inválidos.";
lblMsgErro.visible = true;
loginInput.focus = true;
}
}
The slot LoginController.abrirMain() will load and show the main.qml
void QLoginController::abrirMain()
{
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("MainController", new QMainController(0,m_autenticado));
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
QQuickWindow* window = qobject_cast<QQuickWindow*>(engine.rootObjects().at(0));
window->showFullScreen();
}
The problem is: main.qml is not being shown.
//main.qml
ApplicationWindow {
id: mainWindow
visible: true
modality: "WindowModal"
visibility: "FullScreen"
color: "#09435b"
}
Question: Am I using the right approach to open this new window? If not, how should I do this?
Because you create the engine on stack :), which will be deleted automatically before abrirMain() returns and hence destroying the window too.
You should create engine on heap. Something like this:
// 'this' will become engine's parent and will automaticlaly delete engine when 'this' is deleted
QQmlApplicationEngine *engine = new QQmlApplicationEngine(this);
engine->rootContext()->setContextProperty("MainController", new QMainController(0,m_autenticado));
engine->load(QUrl(QStringLiteral("qrc:///main.qml")));
QQuickWindow* window = qobject_cast<QQuickWindow*>(engine->rootObjects().at(0));
window->showFullScreen();

How to Add a Button or other widget in QTreeWidget Header?

Can anyone give me a hint on how to place a button in the header of QTreeWidget
A minimal example is more than welcome?
For the person in the comments (and basically anyone else stopping by who might be helped by this), here is the translated python version of the code below:
from PySide2 import QtWidgets, QtCore
import sys
class Header(QtWidgets.QHeaderView):
def __init__(self, orientation, parent=None):
super(Header, self).__init__(orientation, parent)
self.button = QtWidgets.QPushButton('Button text', self)
class TreeWidget(QtWidgets.QTreeWidget):
def __init__(self, parent=None):
super(TreeWidget, self).__init__(parent)
item0 = QtWidgets.QTreeWidgetItem(["Item 0"])
item1 = QtWidgets.QTreeWidgetItem(["Item 1"])
self.addTopLevelItem(item0)
self.addTopLevelItem(item1)
self.createHeader()
def createHeader(self):
header = Header(QtCore.Qt.Horizontal, self)
self.setHeader(header)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = TreeWidget()
w.show()
sys.exit(app.exec_())
#QTreeWidget header is an "stand alone" widget - QHeaderView, so you can set the custom implementation of it.
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QHeaderView>
#include <QPushButton>
class Header
: public QHeaderView
{
public:
Header(QWidget* parent)
: QHeaderView(Qt::Horizontal, parent)
, m_button(new QPushButton("Button", this))
{
}
private:
QPushButton* m_button;
};
class TreeWidget
: public QTreeWidget
{
Q_OBJECT
public:
TreeWidget()
: QTreeWidget(0)
{
QTreeWidgetItem* item0 = new QTreeWidgetItem(QStringList("Item 0"));
QTreeWidgetItem* item1 = new QTreeWidgetItem(QStringList("Item 1"));
addTopLevelItem(item0);
addTopLevelItem(item1);
createHeader();
}
private:
void createHeader()
{
m_header = new Header(this);
setHeader(m_header);
}
private:
Header* m_header;
};
int main(int c, char** v)
{
QApplication a(c, v);
TreeWidget* tw = new TreeWidget();
tw->show();
return a.exec();
}
//QTreeWidget header is an "stand alone" widget - QHeaderView, so you can set the custom implementation of it.

How to set non-selectable default text on QComboBox?

Using a regular QComboBox populated with items, if currentIndex is set to -1, the widget is empty. It would be very useful to instead have an initial descriptive text visible in the combo box(e.g. "--Select Country--", "--Choose Topic--", etc.) which is not shown in the dropdown list.
I couldn't find anything in the documentation, nor any previous questions with answers.
It doesn't appear that case was anticipated in the Combo Box API. But with the underlying model flexibility it seems you should be able to add your --Select Country-- as a first "legitimate" item, and then keep it from being user selectable:
QStandardItemModel* model =
qobject_cast<QStandardItemModel*>(comboBox->model());
QModelIndex firstIndex = model->index(0, comboBox->modelColumn(),
comboBox->rootModelIndex());
QStandardItem* firstItem = model->itemFromIndex(firstIndex);
firstItem->setSelectable(false);
Depending on what precise behavior you want, you might want to use setEnabled instead. Or I'd personally prefer it if it was just a different color item that I could set it back to:
Qt, How do I change the text color of one item of a QComboBox? (C++)
(I don't like it when I click on something and then get trapped to where I can't get back where I was, even if it's a nothing-selected-yet-state!)
One way you can do something similar is to set a placeholder:
comboBox->setPlaceholderText(QStringLiteral("--Select Country--"));
comboBox->setCurrentIndex(-1);
This way you have a default that cannot be selected.
Leaving my solution here from PyQt5. Create a proxy model and shift all the rows down one, and return a default value at row 0.
class NullRowProxyModel(QAbstractProxyModel):
"""Creates an empty row at the top for null selections on combo boxes
"""
def __init__(self, src, text='---', parent=None):
super(NullRowProxyModel, self).__init__(parent)
self._text = text
self.setSourceModel(src)
def mapToSource(self, proxyIndex: QModelIndex) -> QModelIndex:
if self.sourceModel():
return self.sourceModel().index(proxyIndex.row()-1, proxyIndex.column())
else:
return QModelIndex()
def mapFromSource(self, sourceIndex: QModelIndex) -> QModelIndex:
return self.index(sourceIndex.row()+1, sourceIndex.column())
def data(self, proxyIndex: QModelIndex, role=Qt.DisplayRole) -> typing.Any:
if proxyIndex.row() == 0 and role == Qt.DisplayRole:
return self._text
elif proxyIndex.row() == 0 and role == Qt.EditRole:
return None
else:
return super(NullRowProxyModel, self).data(proxyIndex, role)
def index(self, row: int, column: int, parent: QModelIndex = ...) -> QModelIndex:
return self.createIndex(row, column)
def parent(self, child: QModelIndex) -> QModelIndex:
return QModelIndex()
def rowCount(self, parent: QModelIndex = ...) -> int:
return self.sourceModel().rowCount()+1 if self.sourceModel() else 0
def columnCount(self, parent: QModelIndex = ...) -> int:
return self.sourceModel().columnCount() if self.sourceModel() else 0
def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any:
if not self.sourceModel():
return None
if orientation == Qt.Vertical:
return self.sourceModel().headerData(section-1, orientation, role)
else:
return self.sourceModel().headerData(section, orientation, role)