Related
i have a problem with pyqt. When clicking on a row in the table, the GroupBox loads and it is correct to load the data. When selecting a different row, all widgets are not properly loaded. Clicking a row again refreshes the tables but does not correctly display the other widgets
class Zlecenia(QWidget):
def __init__(self):
super(Zlecenia, self).__init__()
self.zlecenia = self.pobierz_numery_zlecen()
self.vbox_zlecenia = QVBoxLayout()
self.tabela_widok_zlecenia = QTableView()
self.header_zlecenia = ['Numer zlecenia', 'Klient']
self.tabela_wybrane_zlecenie = QTableView()
self.tabela_wybrane_zlecenie_model = TabelaWybraneZlecenieModel()
self.tabela_widok_zlecenia_model = TabelaZleceniaModel(self.zlecenia, self.header_zlecenia)
self.tabela_widok_zlecenia.setModel(self.tabela_widok_zlecenia_model)
self.tabela_widok_zlecenia.setColumnWidth(0, 400)
self.tabela_widok_zlecenia.horizontalHeader().setStretchLastSection(True)
self.tes = self.tabela_widok_zlecenia.setSelectionBehavior(QTableView.SelectRows)
self.tabela_widok_zlecenia.doubleClicked.connect(self.clicked_zlecenie)
self.groupBox_zleceni = QGroupBox()
self.groupBox_zleceni.setVisible(False)
self.vbox_zlecenia.addWidget(self.tabela_widok_zlecenia)
self.vbox_zlecenia.addWidget(self.groupBox_zleceni)
def clicked_zlecenie(self):
self.index = self.tabela_widok_zlecenia.selectionModel().currentIndex()
self.numer_zlecenia = self.index.siblingAtColumn(0).data()
klient = self.index.siblingAtColumn(1).data()
numer_zlecenia = str(self.numer_zlecenia)
self.groupBox_zleceni.setTitle(klient + ': ' + numer_zlecenia)
hbox_zlecenia = QHBoxLayout()
self.nazwa_klienta_do_zlecenia = klient
self.numer_zlecenia_do_zlecenia = numer_zlecenia
import sqlCommand
self.lista_detali_poj = sqlCommand.DB()
self.lista_detali_poj = self.lista_detali_poj.wczytanie_detali_do_zlecenia(self.numer_zlecenia_do_zlecenia)
self.lista_detali_zlozenie = sqlCommand.DB()
self.lista_detali_zlozenie = self.lista_detali_zlozenie.wczytanie_zlozen_do_zlecenia(self.numer_zlecenia_do_zlecenia)
self.cala_lista = self.lista_detali_poj + self.lista_detali_zlozenie
self.tabela_wybrane_zlecenie_model = TabelaWybraneZlecenieModel(self.cala_lista)
self.tabela_wybrane_zlecenie.setModel(self.tabela_wybrane_zlecenie_model)
self.tabela_wybrane_zlecenie.setColumnWidth(0, 600)
self.tabela_wybrane_zlecenie.horizontalHeader().setStretchLastSection(True)
hbox_zlecenia.addWidget(self.tabela_wybrane_zlecenie)
vbox_zlecenie_button = QVBoxLayout()
button_dodaj_detale = QPushButton('Dodaj \ndetale')
button_dodaj_detale.setFixedSize(180,50)
button_dodaj_detale.clicked.connect(self.dodaj_detale)
vbox_zlecenie_button.addWidget(button_dodaj_detale)
self.button_usun_detal_ze_zlecenia = QPushButton('Usuń \ndetal')
self.button_usun_detal_ze_zlecenia.setFixedSize(180, 50)
self.button_usun_detal_ze_zlecenia.clicked.connect(self.usun_detal_ze_zlecenia)
vbox_zlecenie_button.addWidget(self.button_usun_detal_ze_zlecenia)
self.button_kopiuj_pliki = QPushButton('Kopiuj \npliki')
self.button_kopiuj_pliki.setFixedSize(180, 50)
vbox_zlecenie_button.addWidget(self.button_kopiuj_pliki)
hbox_zlecenia.addLayout(vbox_zlecenie_button)
self.groupBox_zleceni.setLayout(hbox_zlecenia)
self.tabela_widok_zlecenia.setVisible(False)
self.groupBox_zleceni.setVisible(True)
How to properly display widgets after clicking on a table row again?
My Intention is to display five types of labels in two frames (left and right Frame).
In my case, the right frame, which contains two labels, is displayed or added as per my idea.
But in the Left frame, I face a problem. Added widgets display is not proper. The left frame is a GridLayout, In which we add three widgets. As of my idea, top widget is none or single line. Middle widget is a character and the bottim widget is either single, double or triple line.
import sys
from PyQt5.QtWidgets import QWidget,QFrame,QLabel,QHBoxLayout,QVBoxLayout,QGridLayout,QApplication
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
class Left_Right_FrameDesign(QWidget):
def __init__(self,color_left_lrcd,color_right_lrcd,
frame_left_lrcd_width,frame_right_lrcd_width,
frame_lrcd_height):
super().__init__()
self.color_left_lrcd = color_left_lrcd
self.color_right_lrcd = color_right_lrcd
self.frame_left_lrcd_width = frame_left_lrcd_width
self.frame_right_lrcd_width = frame_right_lrcd_width
self.frame_lrcd_height = frame_lrcd_height
self.frame_left_lrcd = QFrame()
self.frame_left_lrcd.setProperty("type","1")
self.frame_left_lrcd.setFixedSize(self.frame_left_lrcd_width,self.frame_lrcd_height)
self.frame_right_lrcd = QFrame()
self.frame_right_lrcd.setProperty("type","1")
self.frame_right_lrcd.setFixedHeight(self.frame_lrcd_height)
self.frame_right_lrcd.setFixedWidth(self.frame_right_lrcd_width)
self.principal_lrcd_layout = QHBoxLayout()
self.frame_left_lrcd_layout = QGridLayout(self.frame_left_lrcd)
self.frame_right_lrcd_layout = QVBoxLayout(self.frame_right_lrcd)
self.frame_left_lrcd_layout.setContentsMargins(0, 0, 0, 0)
self.frame_right_lrcd_layout.setContentsMargins(0,0,0,0)
self.frame_right_lrcd_layout.setSpacing(0)
self.frame_left_lrcd_layout.setAlignment(Qt.AlignHCenter| Qt.AlignVCenter)
self.principal_lrcd_layout.setSpacing(0)
self.principal_lrcd_layout.addWidget(self.frame_left_lrcd)
self.principal_lrcd_layout.addWidget(self.frame_right_lrcd)
self.principal_lrcd_layout.setContentsMargins(0, 0, 0, 0)
class example(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("My Widget Example")
self.setStyleSheet(style_sheet())
my_font_1 = QFont("Arial", 10, QFont.Bold)
my_font_1.setLetterSpacing(QFont.AbsoluteSpacing, -6)
my_font_en = QFont("Arial", 8, QFont.Bold)
my_font_hi = QFont("Mangal", 11, )
my_font_ta = QFont("Latha", 8,QFont.Bold)
lbl_sline = ('\u2015' * 1) # print single line
lbl_dline = '\u003D' * 4 # print double line
lbl_tline = ('\u2261' * 4) # print tripleline
text = {"lbl01": {"lan_1":"en_Accounts","lan_2":"ta_கணக்கியல்" ,"char":"ta_ழ்","top_line":"0","bot_line":"1"},
"lbl02": {"lan_1":"en_Manufacturing","lan_2":"hi_हिसाब किताब","char":"en_F8","top_line":"1","bot_line":"2"},
"lbl03": {"lan_1":"en_Inventory","lan_2":"ta_சரக்கியல்" ,"char":"hi_हि","top_line":"1","bot_line":"3"}}
lst_mainkeys = list(text.keys())
lst_subkeys = list(text[lst_mainkeys[0]].keys())
self.frame_design,self.lbl_lan1,self.lbl_lan2,self.lbl_tline,self.lbl_char,self.lbl_bline = {},{},{},{},{},{}
self.lay = QHBoxLayout()
self.lay.setSpacing(3)
self.lay.setContentsMargins(0, 0, 0, 0)
for i, j in enumerate(lst_mainkeys):
self.frame_design[i] = Left_Right_FrameDesign(color_left_lrcd="green", color_right_lrcd="blue",
frame_left_lrcd_width=50, frame_right_lrcd_width=200,
frame_lrcd_height= 40)
for m,n in enumerate(lst_subkeys):
name = j+"_"+str(m)
self.lbl_lan1[name] = QLabel()
self.lbl_lan2[name] = QLabel()
self.lbl_tline[name] = QLabel()
self.lbl_char[name] = QLabel()
self.lbl_bline[name] = QLabel()
self.temp_name =(text[j][n])
if self.temp_name[:2] == "en":
self.myfont = my_font_en
elif self.temp_name[:2] == "ta":
self.myfont = my_font_ta
elif self.temp_name[:2] == "hi":
self.myfont = my_font_hi
if n == "lan_1":
self.lbl_lan1[name].setText(text[j][n][3:])
self.lbl_lan1[name].setFont(self.myfont)
self.frame_design[i].frame_right_lrcd_layout.addWidget(self.lbl_lan1[name])
elif n == "lan_2":
self.lbl_lan2[name].setText(text[j][n][3:])
self.lbl_lan2[name].setFont(self.myfont)
self.frame_design[i].frame_right_lrcd_layout.addWidget(self.lbl_lan2[name])
elif n == "char":
self.lbl_char[name].setText(text[j][n][3:])
self.lbl_char[name].setFont(self.myfont)
self.frame_design[i].frame_left_lrcd_layout.addWidget(self.lbl_char[name],1,0)
if n == "top_line":
if self.temp_name == 0:
pass
elif self.temp_name == 1:
self.lbl_tline[name].setText(lbl_sline)
self.frame_design[i].frame_left_lrcd_layout.addWidget(self.lbl_tline[name],0,0)
if n == "bot_line":
if self.temp_name == 1:
self.lbl_bline[name].setText(lbl_sline)
elif self.temp_name == 2:
self.lbl_bline[name].setText(lbl_dline)
elif self.temp_name == 3:
self.lbl_bline[name].setText(lbl_tline)
self.lbl_bline[name].setText(text[j][n])
self.frame_design[i].frame_left_lrcd_layout.addWidget(self.lbl_bline[name],2,0)
self.lay.addLayout(self.frame_design[i].principal_lrcd_layout)
self.setLayout(self.lay)
def style_sheet():
return """
QFrame[type="1"] {background-color : rgb(188,212,114);}
QFrame[type="1"]::hover{background-color:rgb(255, 223, 0);color: Black;}}"""
if __name__ =="__main__":
app = QApplication(sys.argv)
mainwindow = example()
mainwindow.show()
sys.exit(app.exec_())
I'm trying to create a grid of square buttons that is scrollable if the window is too small to show all of them. I'd like there to be labels on the left-most column and top-most row showing the button indices.
Is there a way to create a QScrollArea with the widgets (labels) in the top-most row and left-most column "frozen". Similar to how you can freeze rows and columns in an Excel Sheet where they follow the view around as you scroll.
See a mockup here:
Either Qt and PyQt are welcome.
I solved my problem with multiple QScrollAreas using the method outlined in this answer. The idea is to have the frozen areas be QScrollArea with disabled scrolling, while the unfrozen QScrollArea scrollbar signals are connected to the frozen QScrollArea scrollbar slots.
Here is the code of my mockup with the top-most row and left-most column frozen. The especially relevant parts are the FrozenScrollArea class and the connections inside the Window class.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication,
QPushButton,
QWidget,
QScrollArea,
QGridLayout,
QLabel,
QFrame,
QSpacerItem,
QSizePolicy,
)
ROWS = 10
COLS = 20
SIZE = 35
style = """
Button {
padding: 0;
margin: 0;
border: 1px solid black;
}
Button::checked {
background-color: lightgreen;
}
"""
class Button(QPushButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setFixedSize(SIZE, SIZE)
self.setCheckable(True)
self.setStyleSheet(style)
class Label(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setAlignment(Qt.AlignCenter)
self.setFixedSize(SIZE, SIZE)
class Labels(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
layout = QGridLayout()
layout.setHorizontalSpacing(0)
layout.setVerticalSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
class FrozenScrollArea(QScrollArea):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWidgetResizable(True)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.verticalScrollBar().setEnabled(False)
self.horizontalScrollBar().setEnabled(False)
class FrozenRow(FrozenScrollArea):
def __init__(self, parent):
super().__init__()
labels = Labels(parent)
for c in range(COLS):
label = Label(self, text = str(c))
labels.layout().addWidget(label, 0, c, 1, 1, Qt.AlignCenter)
labels.layout().addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, COLS, 1, 1)
self.setFrameShape(QFrame.NoFrame)
self.setFixedHeight(SIZE)
self.setWidget(labels)
class FrozenColumn(FrozenScrollArea):
def __init__(self, parent):
super().__init__()
labels = Labels(parent)
for r in range(ROWS):
label = Label(self, text = str(r))
labels.layout().addWidget(label, r, 0, 1, 1, Qt.AlignCenter)
labels.layout().addItem(QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), ROWS, 0, 1, 1)
self.setFrameShape(QFrame.NoFrame)
self.setFixedWidth(SIZE)
self.setWidget(labels)
class ButtonGroup(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
layout = QGridLayout()
for r in range(ROWS):
for c in range(COLS):
button = Button(self)
layout.addWidget(button, r, c, 1, 1)
layout.setHorizontalSpacing(0)
layout.setVerticalSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
class Buttons(QScrollArea):
def __init__(self, parent):
super().__init__()
self.setFrameShape(QFrame.NoFrame)
self.setWidget(ButtonGroup(parent))
class Window(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# layout
layout = QGridLayout()
self.setLayout(layout)
layout.setHorizontalSpacing(0)
layout.setVerticalSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
# frozen row (top)
self.frozenRow = FrozenRow(self)
layout.addWidget(self.frozenRow, 0, 1, 1, 1)
# frozen column (left)
self.frozenColumn = FrozenColumn(self)
layout.addWidget(self.frozenColumn, 1, 0, 1, 1)
# button grid
self.buttons = Buttons(self)
layout.addWidget(self.buttons, 1, 1, 1, 1)
# scrollbar connections
self.buttons.horizontalScrollBar().valueChanged.connect(self.frozenRow.horizontalScrollBar().setValue) # horizontal scroll affects frozen row only
self.buttons.verticalScrollBar().valueChanged.connect(self.frozenColumn.verticalScrollBar().setValue) # vertical scroll affects frozemn column only
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec())
While the frozen scroll area method is effective, it has some drawbacks; most importantly, it:
is not dynamic;
does not consider basic box layouts;
does not support different directions (for boxed layouts) or origin points (for grid layouts);
While this is more a "fringe case", I'd like to suggest an alternative, based on QHeaderView and a "private" model that uses the layout manager for the header sizes.
It doesn't directly support resizing as one would expect from a standard QHeaderView, but that's almost impossible: for boxed layouts it's not possible to set a layout item size (if not by completely overriding the way the layout sets geometries), and for grid layouts there's no way to know if rows or columns are "actually" removed, since rowCount() and columnCount() are never updated dynamically when the grid size changes.
The concept is based on overriding the event filter of the scroll area and check whether geometry changes are happening and if the layout has to lay out items again. Then, the implementation uses the layout information to update the underlying model and provide appropriate values for the SizeHintRole for headerData().
The subclassed QScrollArea creates two QHeaderViews and updates them whenever required using the ResizeToContents section resize mode (which queries headerData()) and uses setViewportMargins based on the size hints of the headers.
class LayoutModel(QtCore.QAbstractTableModel):
reverse = {
QtCore.Qt.Horizontal: False,
QtCore.Qt.Vertical: False
}
def __init__(self, rows=None, columns=None):
super().__init__()
self.rows = rows or []
self.columns = columns or []
def setLayoutData(self, hSizes, vSizes, reverseH=False, reverseV=False):
self.beginResetModel()
self.reverse = {
QtCore.Qt.Horizontal: reverseH,
QtCore.Qt.Vertical: reverseV
}
self.rows = vSizes
self.columns = hSizes
opt = QtWidgets.QStyleOptionHeader()
opt.text = str(len(vSizes))
style = QtWidgets.QApplication.style()
self.headerSizeHint = style.sizeFromContents(style.CT_HeaderSection, opt, QtCore.QSize())
self.endResetModel()
def rowCount(self, parent=None):
return len(self.rows)
def columnCount(self, parent=None):
return len(self.columns)
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if self.reverse[orientation]:
if orientation == QtCore.Qt.Horizontal:
section = len(self.columns) - 1 - section
else:
section = len(self.rows) - 1 - section
# here you can add support for custom header labels
return str(section + 1)
elif role == QtCore.Qt.SizeHintRole:
if orientation == QtCore.Qt.Horizontal:
return QtCore.QSize(self.columns[section], self.headerSizeHint.height())
return QtCore.QSize(self.headerSizeHint.width(), self.rows[section])
def data(self, *args, **kwargs):
pass # not really required, but provided for consistency
class ScrollAreaLayoutHeaders(QtWidgets.QScrollArea):
_initialized = False
def __init__(self):
super().__init__()
self.hHeader = QtWidgets.QHeaderView(QtCore.Qt.Horizontal, self)
self.vHeader = QtWidgets.QHeaderView(QtCore.Qt.Vertical, self)
self.layoutModel = LayoutModel()
for header in self.hHeader, self.vHeader:
header.setModel(self.layoutModel)
header.setSectionResizeMode(header.Fixed)
self.updateTimer = QtCore.QTimer(
interval=0, timeout=self.updateHeaderSizes, singleShot=True)
def layout(self):
try:
return self.widget().layout()
except AttributeError:
pass
def eventFilter(self, obj, event):
if obj == self.widget() and obj.layout() is not None:
if event.type() in (event.Resize, event.Move):
if self.sender() in (self.verticalScrollBar(), self.horizontalScrollBar()):
self.updateGeometries()
else:
self.updateHeaderSizes()
elif event.type() == event.LayoutRequest:
self.widget().adjustSize()
self.updateTimer.start()
return super().eventFilter(obj, event)
def updateHeaderSizes(self):
layout = self.layout()
if layout is None:
self.layoutModel.setLayoutData([], [])
self.updateGeometries()
return
self._initialized = True
hSizes = []
vSizes = []
layGeo = self.widget().rect()
reverseH = reverseV = False
if isinstance(layout, QtWidgets.QBoxLayout):
count = layout.count()
direction = layout.direction()
geometries = [layout.itemAt(i).geometry() for i in range(count)]
# LeftToRight and BottomToTop layouts always have a first bit set
reverse = direction & 1
if reverse:
geometries.reverse()
lastPos = 0
lastGeo = geometries[0]
if layout.direction() in (layout.LeftToRight, layout.RightToLeft):
if reverse:
reverseH = True
vSizes.append(layGeo.bottom())
lastExt = lastGeo.x() + lastGeo.width()
for geo in geometries[1:]:
newPos = lastExt + (geo.x() - lastExt) / 2
hSizes.append(newPos - lastPos)
lastPos = newPos
lastExt = geo.x() + geo.width()
hSizes.append(layGeo.right() - lastPos - 1)
else:
if reverse:
reverseV = True
hSizes.append(layGeo.right())
lastExt = lastGeo.y() + lastGeo.height()
for geo in geometries[1:]:
newPos = lastExt + (geo.y() - lastExt) / 2
vSizes.append(newPos - lastPos)
lastPos = newPos
lastExt = geo.y() + geo.height()
vSizes.append(layGeo.bottom() - lastPos + 1)
else:
# assume a grid layout
origin = layout.originCorner()
if origin & 1:
reverseH = True
if origin & 2:
reverseV = True
first = layout.cellRect(0, 0)
lastX = lastY = 0
lastRight = first.x() + first.width()
lastBottom = first.y() + first.height()
for c in range(1, layout.columnCount()):
cr = layout.cellRect(0, c)
newX = lastRight + (cr.x() - lastRight) / 2
hSizes.append(newX - lastX)
lastX = newX
lastRight = cr.x() + cr.width()
hSizes.append(layGeo.right() - lastX)
for r in range(1, layout.rowCount()):
cr = layout.cellRect(r, 0)
newY = lastBottom + (cr.y() - lastBottom) / 2
vSizes.append(newY - lastY)
lastY = newY
lastBottom = cr.y() + cr.height()
vSizes.append(layGeo.bottom() - lastY)
hSizes[0] += 2
vSizes[0] += 2
self.layoutModel.setLayoutData(hSizes, vSizes, reverseH, reverseV)
self.updateGeometries()
def updateGeometries(self):
self.hHeader.resizeSections(self.hHeader.ResizeToContents)
self.vHeader.resizeSections(self.vHeader.ResizeToContents)
left = self.vHeader.sizeHint().width()
top = self.hHeader.sizeHint().height()
self.setViewportMargins(left, top, 0, 0)
vg = self.viewport().geometry()
self.hHeader.setGeometry(vg.x(), 0,
self.viewport().width(), top)
self.vHeader.setGeometry(0, vg.y(),
left, self.viewport().height())
self.hHeader.setOffset(self.horizontalScrollBar().value())
self.vHeader.setOffset(self.verticalScrollBar().value())
def sizeHint(self):
if not self._initialized and self.layout():
self.updateHeaderSizes()
hint = super().sizeHint()
if self.widget():
viewHint = self.viewportSizeHint()
if self.horizontalScrollBarPolicy() == QtCore.Qt.ScrollBarAsNeeded:
if viewHint.width() > hint.width():
hint.setHeight(hint.height() + self.horizontalScrollBar().sizeHint().height())
if self.verticalScrollBarPolicy() == QtCore.Qt.ScrollBarAsNeeded:
if viewHint.height() > hint.height():
hint.setWidth(hint.width() + self.verticalScrollBar().sizeHint().width())
hint += QtCore.QSize(
self.viewportMargins().left(), self.viewportMargins().top())
return hint
def resizeEvent(self, event):
super().resizeEvent(event)
QtCore.QTimer.singleShot(0, self.updateGeometries)
Notes:
the code above will cause some level of recursion; that is expected, as resizing the viewport will obviously trigger a resizeEvent, but Qt is smart enough to ignore them whenever sizes are unchanged;
this will only work for basic QBoxLayouts and QGridLayout; it's untested for QFormLayout and the behavior of other custom QLayout subclasses is completely unexpected;
I am trying to dynamically load a wxGrid with a pandas Dataframe depending on what table is selected in the combobox. I can get the grid to load on intialization, but can't figure out how to get refresh the GridTableClass and grid. Right now I am trying to test with a random Dataframe.
class PageOne(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
#wx.StaticText(self, -1, "This is a PageOne object", (20,20))
class PageTwo(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
#wx.StaticText(self, -1, "This is a PageTwo object", (40, 40))
class PageThree(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
#wx.StaticText(self, -1, "This is a PageThree object", (60, 60))
class DataTable(gridlib.GridTableBase):
def __init__(self, data):
gridlib.GridTableBase.__init__(self)
self.data = data
#self.colnames = colnames
#Store the row and col length to see if table has changed in size
self._rows = self.GetNumberRows()
self._cols = self.GetNumberCols()
self.odd=gridlib.GridCellAttr()
self.odd.SetBackgroundColour((217,217,217))
self.even=gridlib.GridCellAttr()
self.even.SetBackgroundColour((255,255,255))
def GetAttr(self, row, col, kind):
attr = [self.even, self.odd][row % 2]
attr.IncRef()
return attr
def GetNumberRows(self):
return len(self.data)
def GetNumberCols(self):
return len(self.data.columns) + 1
def IsEmptyCell(self, row, col):
return False
def GetValue(self, row, col):
#if col == 0:
# return None #self.data.index[row]
return self.data.iloc[row, col-1]
def SetValue(self, row, col, value):
self.data.iloc[row, col - 1] = value
def GetColLabelValue(self, col):
if col == 0:
return None
#pass
#return 'Index' if self.data.index.name is None else self.data.index.name
return self.data.columns[col - 1] #[col-1]
#return None
#---------------------------------------------------------------------------
class DataGrid(gridlib.Grid):
def __init__(self, parent, data): # data
gridlib.Grid.__init__(self, parent, - 1) #,colnames,-1 # data
#data = pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD'))
#data.reset_index(drop=True, inplace=True)
table = DataTable(data)
print ("passed")
# The second parameter means that the grid is to take ownership of the
# table and will destroy it when done. Otherwise you would need to keep
# a reference to it and call it's Destroy method later.
self.SetTable(table, True)
self.Bind(gridlib.EVT_GRID_CELL_RIGHT_CLICK, self.OnCellRightClick)
def OnCellRightClick(self, event):
print ("OnCellRightClick: (%d,%d)\n" % (event.GetRow(), event.GetCol()))
#-------------------------------------------------------------------------------
class MainFrame(wx.Frame):
def __init__(self, parent, data): # (self, parent, data):
wx.Frame.__init__(self, parent, -1, "Varkey Foundation") #, size=(640,480))
#Create a panel
self.p = wx.Panel(self)
self.Maximize(True)
#Create blank dataframe
data = pd.DataFrame() #pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD')
#data = pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD'))
#data.reset_index(drop=True, inplace=True)
self.data = DataTable(data)
self.nb = wx.Notebook(self.p)
self.p.SetBackgroundColour( wx.Colour( 0, 0, 0 ) ) # 38,38,38
self.nb.SetBackgroundColour(wx.Colour(58, 56, 56) )
#self.SetBackgroundColour( wx.Colour( 255, 255, 56 ) )
#create the page windows as children of the notebook
self.page1 = PageOne(self.nb)
self.page2 = PageTwo(self.nb)
self.page3 = PageThree(self.nb)
# add the pages to the notebook with the label to show on the tab
self.nb.AddPage(self.page1, "Data")
self.nb.AddPage(self.page2, "Analyze")
self.nb.AddPage(self.page3, "Change Log")
#Create the grid and continue layout
self.grid = DataGrid(self.page1, data)
#grid.SetReadOnly(5,5, True)
#CreateFonts
self.b_font = wx.Font(14,wx.ROMAN,wx.NORMAL,wx.BOLD, True)
self.lbl_font = wx.Font(14,wx.ROMAN,wx.NORMAL,wx.NORMAL, True)
self.cb_font = wx.Font(11,wx.SCRIPT,wx.ITALIC,wx.NORMAL, True)
self.h_font = wx.Font(18,wx.DECORATIVE,wx.ITALIC,wx.BOLD, True)
#Create Title bmp
ico = wx.Icon('varkey_bmp.bmp', wx.BITMAP_TYPE_ICO) #'varkey_frame.bmp'
self.SetIcon(ico)
#Page 1 sizers and widgets
self.title = wx.StaticText(self.page1,label="TITLE",style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE)
self.title.SetForegroundColour((255,255,255))
self.title.SetFont(self.h_font)
self.p1_sizer = wx.BoxSizer(wx.VERTICAL)
self.p1_sizer.Add(self.title,0,wx.EXPAND,5)
self.p1_sizer.Add(self.grid,3,wx.EXPAND | wx.ALL ,25)
#self.p1_sizer.Add(self.btn_new,-0,wx.ALIGN_CENTER,5)
self.page1.SetSizer(self.p1_sizer)
#Page 2 sizers and widgets
self.analyze_grid = gridlib.Grid(self.page2)
self.analyze_grid.CreateGrid(0, 10)
self.p2_sizer = wx.BoxSizer(wx.VERTICAL)
self.p2_sizer.Add(self.analyze_grid,1,wx.EXPAND)
self.page2.SetSizer(self.p2_sizer)
#Page 3 sizers and widgets
self.log_grid = gridlib.Grid(self.page3)
self.log_grid.CreateGrid(0, 9)
self.log_grid.EnableEditing(False)
self.p3_sizer = wx.BoxSizer(wx.VERTICAL)
self.p3_sizer.Add(self.log_grid,1,wx.EXPAND)
self.page3.SetSizer(self.p3_sizer)
#Create widgets for top sizer
#Insert Image
self.staticbitmap = wx.StaticBitmap(self.p)
self.staticbitmap.SetBitmap(wx.Bitmap('varkey_logo2.jpg'))
self
self.lbl_user = wx.StaticText(self.p,label="Username:")
self.lbl_password = wx.StaticText(self.p,label="Password:")
self.lbl_interaction = wx.StaticText(self.p,label="Interaction:")
self.lbl_table = wx.StaticText(self.p,label="Table:")
#SetForground colors
self.lbl_user.SetForegroundColour((255,255,255))
self.lbl_password.SetForegroundColour((255,255,255))
self.lbl_interaction.SetForegroundColour((255,255,255))
self.lbl_table.SetForegroundColour((255,255,255))
#Set Fonts
self.lbl_user.SetFont(self.lbl_font)
self.lbl_password.SetFont(self.lbl_font)
self.lbl_interaction.SetFont(self.lbl_font)
self.lbl_table.SetFont(self.lbl_font)
self.tc_user =wx.TextCtrl(self.p,value='cmccall95',size = (130,25))
self.tc_password =wx.TextCtrl(self.p,value='Achilles95', style=wx.TE_PASSWORD | wx.TE_PROCESS_ENTER,size = (130,25))
#self.tc_password.Bind(wx.EVT_TEXT_ENTER,self.onLogin)
self.tc_user.SetFont(self.cb_font)
self.tc_password.SetFont(self.cb_font)
self.btn_login = wx.Button(self.p,label="Login", size=(105,30))
self.btn_login.SetBackgroundColour(wx.Colour(198, 89, 17))
self.btn_login.SetFont(self.b_font)
self.btn_login.Bind(wx.EVT_BUTTON, self.onLogin) #connect_mysql
self.btn_logout = wx.Button(self.p,label="Logout",size=(105,30))
self.btn_logout.SetBackgroundColour(wx.Colour(192,0,0))
self.btn_logout.SetFont(self.b_font)
#self.btn_logout.Bind(wx.EVT_BUTTON, self.onLogout)
self.combo_interaction = wx.ComboBox(self.p, size = (160,25),style = wx.CB_READONLY | wx.CB_SORT | wx.CB_SORT)
#self.combo_interaction.Bind(wx.EVT_COMBOBOX, self.onComboInteraction)
self.combo_table = wx.ComboBox(self.p, size = (160,25),style = wx.CB_READONLY | wx.CB_SORT | wx.CB_SORT)
#self.combo_table.Bind(wx.EVT_COMBOBOX, self.onHideCommands)
self.combo_interaction.SetFont(self.cb_font)
self.combo_table.SetFont(self.cb_font)
#self.combo_table.Bind(wx.EVT_COMBOBOX ,self.OnComboTable)
self.btn_load = wx.Button(self.p,label="Load Table", size=(105,30))
self.btn_load.SetBackgroundColour(wx.Colour(31, 216, 6))
self.btn_load.SetFont(self.b_font)
#self.btn_load.Bind(wx.EVT_BUTTON, self.onLoadData)
self.btn_load.Bind(wx.EVT_BUTTON, self.test_return)
self.lc_change = wx.ListCtrl(self.p,-1,style = wx.TE_MULTILINE | wx.LC_REPORT | wx.LC_VRULES)
self.lc_change.InsertColumn(0,"User ID")
self.lc_change.InsertColumn(1,"Status")
self.lc_change.InsertColumn(2,"Description")
self.lc_change.InsertColumn(3,"Date/Time")
#Set column widths
self.lc_change.SetColumnWidth(0, 75)
self.lc_change.SetColumnWidth(1, 75)
self.lc_change.SetColumnWidth(2, 450)
self.lc_change.SetColumnWidth(3, 125)
#Create Filler text
self.lbl_filler = wx.StaticText(self.p,label="",size = (125,20))
#Create FlexGridSizers(For top half)
self.left_fgs = wx.FlexGridSizer(3,4,25,15)
self.left_fgs.AddMany([(self.lbl_user,1,wx.ALIGN_LEFT | wx.LEFT,15),(self.tc_user,1,wx.EXPAND),(self.lbl_interaction,1,wx.ALIGN_RIGHT|wx.RIGHT, 10),(self.combo_interaction,1,wx.EXPAND),
(self.lbl_password,1,wx.ALIGN_LEFT| wx.LEFT,15),(self.tc_password,1,wx.EXPAND),(self.lbl_table,1,wx.ALIGN_RIGHT|wx.RIGHT, 10),(self.combo_table),
(self.btn_login,2,wx.EXPAND),(self.btn_logout,1,wx.EXPAND),(self.lbl_filler,1,wx.EXPAND),(self.btn_load,1)])
#Create Top Sizer
self.top_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.top_sizer.Add(self.left_fgs,proportion = 1, flag = wx.ALL|wx.EXPAND,border = 30)
self.top_sizer.Add(self.staticbitmap,2,wx.TOP | wx.RIGHT, border = 40) #30
self.top_sizer.Add(self.lc_change,2,wx.RIGHT|wx.EXPAND ,30)
#create Bottom Sizer
self.bottom_sizer = wx.BoxSizer(wx.VERTICAL)
self.bottom_sizer.Add(self.nb,proportion = 5, flag = wx.LEFT |wx.RIGHT | wx.EXPAND,border = 30)
self.mainsizer = wx.BoxSizer(wx.VERTICAL)
self.mainsizer.Add(self.top_sizer,proportion = 0, flag = wx.ALL|wx.EXPAND,border = 5)
self.mainsizer.Add(self.bottom_sizer,proportion = 1,flag = wx.ALL|wx.EXPAND,border = 5)
#self.mainsizer.Add(self.status_sizer,proportion =0,flag = wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL, border = 15)
self.p.SetSizerAndFit(self.mainsizer)
def test_reload(self, event):
data = pd.DataFrame() #pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD')
data = pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD'))
#data.reset_index(drop=True, inplace=True)
#self.data = DataTable(data)
self.table.data = self.data
self.grid.Refresh()
#Some more functions.......
if __name__ == '__main__':
import sys
app = wx.App()
frame = MainFrame(None, sys.stdout) # (None, sys.stdout)
frame.Show(True)
app.MainLoop()
I've tried many different variations of the function and can't understand how this should work. From my understanding, I should the table and then call the grid to refresh.
def test_reload(self, event):
data = pd.DataFrame() #pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD')
data = pd.DataFrame(np.random.randint(0,100,size=(200, 5)),columns=list('EFGHD'))
self.table.data = self.data
self.grid.Refresh()
I managed to find a solution. Hopefully this helps someone else out.
To reload the grid, I first create new data. Then I destroy the grid, create a new grid, and insert it in place of the old grid. You must then call Layout(). The Freeze() and Thaw() methods are to prevent screen flickering.
def test_reload(self, event):
self.Freeze()
#Create new data
data = pd.DataFrame(np.random.randint(0,100,size=(40000, 5)),columns=list('EFGHD'))
#Destroy and create grid with new data assigned
self.grid.Destroy()
self.grid = DataGrid(self.page1, data)
#Insert grid into existing sizer
self.p1_sizer.Insert(1,self.grid,1,wx.RIGHT| wx.LEFT|wx.EXPAND, 20)
self.p1_sizer.Layout()
self.Thaw()
I'm sure there is a better method out there, but this works and is instant.
Kivy Gui have a transition animation to switch between windows (going back and forth also) we can do it easily in kivy. But in PyQt5 I did't find out any way to transit between window (with animation) and going back and forth to a window again and again is also not working. So, is there any way to do like Kivy do transitions, going back and forth to a window easily in PyQt5.
Qt doesn't provide a similar effect on its own, but it still can be achieved using a subclass of a QStackedWidget (which behaves similarly to a QTabWidget, but without any QTabBar).
In the following example I'll show you how to implement a basic "swap" transition between two widgets that are added to a QStackedWidget, the next widget will scroll from right to left if the index is greater than the current, and vice versa.
class TransitionWidget(QtWidgets.QStackedWidget):
_nextIndex = _nextWidget = None
_orientation = QtCore.Qt.Horizontal
def __init__(self):
super().__init__()
self._animation = QtCore.QVariantAnimation(
startValue=0., endValue=1., duration=250)
self._animation.valueChanged.connect(self._aniUpdate)
self._animation.finished.connect(self._aniFinished)
self._animation.setEasingCurve(QtCore.QEasingCurve.InOutQuart)
def setDuration(self, duration):
self._animation.setDuration(duration)
def setCurve(self, curve):
if isinstance(curve, QtCore.QEasingCurve):
self._animation.setEasingCurve(curve)
def setOrientation(self, orientation):
self._orientation = orientation
def getRange(self, prevIndex, nextIndex):
rect = self.rect()
currentStart = nextEnd = QtCore.QPoint()
if self._orientation == QtCore.Qt.Horizontal:
if prevIndex < nextIndex:
currentEnd = QtCore.QPoint(-rect.width(), 0)
nextStart = QtCore.QPoint(rect.width(), 0)
else:
currentEnd = QtCore.QPoint(rect.width(), 0)
nextStart = QtCore.QPoint(-rect.width(), 0)
else:
if prevIndex < nextIndex:
currentEnd = QtCore.QPoint(0, -rect.width())
nextStart = QtCore.QPoint(0, rect.width())
else:
currentEnd = QtCore.QPoint(0, rect.width())
nextStart = QtCore.QPoint(0, -rect.width())
return currentStart, currentEnd, nextStart, nextEnd
def setCurrentIndex(self, index):
if index == self.currentIndex():
return
# prepare the next widget changes
if self._nextWidget is not None:
self._nextWidget.hide()
self._nextIndex = index
self._nextWidget = self.widget(index)
self._nextWidget.show()
rect = self.rect()
rect.translate(self.rect().topRight())
self._nextWidget.setGeometry(rect)
self._nextWidget.raise_()
self._animation.start()
def _aniFinished(self):
super().setCurrentIndex(self._nextIndex)
self._nextIndex = self._nextWidget = None
def _aniUpdate(self, value):
if not self._animation.state():
return
currentStart, currentEnd, nextStart, nextEnd = self.getRange(self.currentIndex(), self._nextIndex)
rect = self.rect()
self.currentWidget().setGeometry(rect.translated(QtCore.QLineF(currentStart, currentEnd).pointAt(value).toPoint()))
self._nextWidget.setGeometry(rect.translated(QtCore.QLineF(nextStart, nextEnd).pointAt(value).toPoint()))
self.update()
Example code:
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
mainWidget = QtWidgets.QWidget()
mainLayout = QtWidgets.QHBoxLayout(mainWidget)
transitionWidget = TransitionWidget()
mainLayout.addWidget(transitionWidget)
pageCount = 10
for page in range(pageCount):
widget = QtWidgets.QWidget()
layout = QtWidgets.QGridLayout(widget)
pageLabel = QtWidgets.QLabel('Page {}'.format(page + 1))
layout.addWidget(pageLabel, 0, 0, 1, 2)
prevBtn = QtWidgets.QPushButton('Previous')
if not page:
prevBtn.setEnabled(False)
layout.addWidget(prevBtn)
nextBtn = QtWidgets.QPushButton('Next')
layout.addWidget(nextBtn)
if page == pageCount - 1:
nextBtn.setEnabled(False)
transitionWidget.addWidget(widget)
prevBtn.clicked.connect(lambda _, page=page: transitionWidget.setCurrentIndex(page - 1))
nextBtn.clicked.connect(lambda _, page=page: transitionWidget.setCurrentIndex(page + 1))
sep = QtWidgets.QFrame(frameShape=QtWidgets.QFrame.VLine)
mainLayout.addWidget(sep)
orientationCombo = QtWidgets.QComboBox()
orientationLayout = QtWidgets.QFormLayout()
mainLayout.addLayout(orientationLayout)
orientationCombo.addItems(['Horizontal', 'Vertical'])
orientationCombo.currentIndexChanged.connect(lambda o: transitionWidget.setOrientation(o + 1))
orientationLayout.addRow('Orientation', orientationCombo)
durationSpin = QtWidgets.QSpinBox(minimum=50, maximum=1000, singleStep=50, suffix='ms')
orientationLayout.addRow('Duration', durationSpin)
durationSpin.setValue(transitionWidget._animation.duration())
durationSpin.valueChanged.connect(transitionWidget.setDuration)
mainWidget.show()
sys.exit(app.exec_())