如何用主窗口自动调整Qwidget的大小?
How to automatically adjust size of Qwidget with mainwindow?
我正在编写一个待办事项列表应用程序以显示一周的每日(24 小时格式)时间表,这是我的设计:
- 在buttom层是一个在vboxlayout中使用QtableWidget的时间框架
- Qtablewidget 的重叠,我使用 QLabels 显示 schedule.Qlabels 未在任何布局中组织。
当主窗口调整大小时,由于vboxlayout,按钮层可以调整其大小。我想知道的是如何在调整按钮层大小时调整顶层大小。或任何其他方法来实现我的想法?我覆盖了 resizeEvent 但失败了。
import sys
from PyQt5 import QtWidgets,QtGui,QtCore
from PyQt5.QtWidgets import QApplication,QWidget,QFrame,QMainWindow,QLabel,QTableWidget,QVBoxLayout
from PyQt5.QtGui import QPainter,QFont,QBrush,QPen
from PyQt5.QtCore import Qt,QEvent
StyleSheet = '''
#central {
border: 0px;
background: gray;
}
QLabel {
border: 1px solid #C0C0C0;
background: #CCFF99;
font: 8pt Comic Sans MS;
border-radius: 4px;
}
#mainFrame {
border: 1px solid gray;
background: white;
}
QTableWidget {
background-color: white;
border: 0.5px solid #C0C0C0;
color: #F0F0F0;
gridline-color: #C0C0C0;
}
QHeaderView::section {
background-color: #FFD800;
padding: 0px;
font-size: 8pt;
border-style: none;
border-bottom: 1px solid #fffff8;
border-right: 1px solid #fffff8;
}
QHeaderView::section:horizontal
{
border-top: 1px solid #fffff8;
}
QHeaderView::section:vertical
{
border-left: 1px solid #fffff8;
font: 6pt Comic Sans MS;
}
QTableWidget::item::hover {
background-color: gray;
border: 0.5px solid #148CD2;
}
'''
class JobLabel(QLabel):
def __init__(self,parent):
QLabel.__init__(self,parent)
def enterEvent(self, event):
self.setStyleSheet("background-color: #99CCFF;")
def leaveEvent(self, event):
self.setStyleSheet("background-color: #CCFF99;")
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.left=100
self.top=100
self.width=1368
self.height=900
self.initUI()
def initUI(self):
self.setWindowTitle("Painting")
self.resize(1368,900)
self.setObjectName("mainWindow")
self.setStyleSheet(StyleSheet)
self.centralWidget=QWidget()
self.centralWidget.resize(self.width,self.height)
self.centralWidget.setObjectName("central")
mainlayout=QVBoxLayout()
ColumnCount=24
RowCount=7
JobBarHeight=20
self.table = QTableWidget(self.centralWidget)
self.table.move(0,0)
self.table.resize(self.centralWidget.width(),self.centralWidget.height())
self.table.setColumnCount(ColumnCount)
self.table.setRowCount(RowCount)
self.table.horizontalHeader().setVisible(True)
self.table.verticalHeader().setVisible(True)
self.table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
RowHeight=(self.table.size().height()-self.table.horizontalHeader().height())/RowCount
ColumnWidth=(self.table.size().width() - self.table.verticalHeader().width())/ColumnCount
hheaders = []
for i in range(1,ColumnCount+1):
if i<10:
hheaders.append("0{}:00".format(i))
else:
hheaders.append("{}:00".format(i))
self.table.setHorizontalHeaderLabels(hheaders)
for i in range(ColumnCount):
self.table.setColumnWidth(i, ColumnWidth)
vheaders = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
self.table.setVerticalHeaderLabels(vheaders)
for i in range(RowCount):
self.table.setRowHeight(i, RowHeight)
mainlayout.addWidget(self.table)
self.centralWidget.setLayout(mainlayout)
self.setCentralWidget(self.centralWidget)
self.job_run_times=[["Sun 12:00","Sun 14:35"],["Fri 22:00","Sat 4:35"],["Tue 1:00","Tue 6:00"],["Wed 17:00","Thu 19:00"],["Wed 18:00","Wed 21:30"],
["Mon 22:00","Mon 23:30"],["Sat 22:00","Mon 3:30"],["Sun 12:00","Sun 14:35"],["Fri 22:00","Sat 4:35"],["Mon 3:00","Mon 6:00"],
["Tue 1:00","Tue 6:00"],["Wed 17:00","Wed 19:30"],["Mon 17:00","Thu 19:00"],["Wed 18:00","Wed 21:30"],["Mon 4:00","Mon 7:00"],
["Mon 22:00","Mon 23:30"],["Sat 22:00","Mon 3:30"]]
#job count
n=0
#label count
k=0
self.label_list=[]
for job_run_time in self.job_run_times:
start=job_run_time[0]
end=job_run_time[1]
weekday_start=start.split()[0]
time_start=start.split()[1]
weekday_end=end.split()[0]
time_end=end.split()[1]
for i in range(RowCount):
if weekday_start==vheaders[i]:
Start_RowCount=i
if weekday_end==vheaders[i]:
End_RowCount=i
Start_ColCount=int(time_start.split(":")[0])+round(int(time_start.split(":")[1])/60,2)
End_ColCount=int(time_end.split(":")[0])+round(int(time_end.split(":")[1])/60,2)
#job time span multiple days
DaySpan=End_RowCount-Start_RowCount
if DaySpan==0:
label_name="label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if Start_RowCount==self.label_list[j][3]:
if (Start_ColCount>=self.label_list[j][5] and Start_ColCount<=self.label_list[j][6]) or (self.label_list[j][5]<=End_ColCount and End_ColCount<=self.label_list[j][6]):
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, Start_RowCount,overlap_jobbar_count,Start_ColCount, End_ColCount
self.label_list.append(label_pos)
k=k+1
n = n + 1
elif DaySpan>0:
label_name = "label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if Start_RowCount==self.label_list[j][3]:
if (Start_ColCount>=self.label_list[j][5] and Start_ColCount<=self.label_list[j][6]) :
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, Start_RowCount, overlap_jobbar_count, Start_ColCount, ColumnCount
self.label_list.append(label_pos)
k = k + 1
for i in range(DaySpan-1):
label_name="label_{}".format(k)
label_pos = []
overlap_jobbar_count = 1
for j in range(len(self.label_list)):
if Start_RowCount+i+1 == self.label_list[j][3]:
overlap_jobbar_count = overlap_jobbar_count + 1
label_pos = n, k, label_name, Start_RowCount + i + 1, overlap_jobbar_count, 0, ColumnCount
self.label_list.append(label_pos)
k=k+1
label_name="label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if End_RowCount==self.label_list[j][3] and End_ColCount>=self.label_list[j][5]:
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, End_RowCount,overlap_jobbar_count, 0, End_ColCount
self.label_list.append(label_pos)
k=k+1
n = n + 1
else:
label_name = "label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if Start_RowCount==self.label_list[j][3]:
if Start_ColCount>=self.label_list[j][5] and Start_ColCount<=self.label_list[j][6]:
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, Start_RowCount, overlap_jobbar_count, Start_ColCount, ColumnCount
self.label_list.append(label_pos)
k = k + 1
for i in range(6-Start_RowCount):
label_name="label_{}".format(k)
label_pos = []
overlap_jobbar_count = 1
for j in range(len(self.label_list)):
if Start_RowCount+i+1 == self.label_list[j][3]:
overlap_jobbar_count = overlap_jobbar_count + 1
label_pos = n, k, label_name, Start_RowCount + i + 1, overlap_jobbar_count, 0, ColumnCount
self.label_list.append(label_pos)
k=k+1
for i in range(End_RowCount):
label_name="label_{}".format(k)
label_pos = []
overlap_jobbar_count = 1
for j in range(len(self.label_list)):
if Start_RowCount+i + 1 == self.label_list[j][3]:
overlap_jobbar_count = overlap_jobbar_count + 1
label_pos = n, k, label_name, Start_RowCount + i + 1, overlap_jobbar_count, 0, ColumnCount
self.label_list.append(label_pos)
k=k+1
label_name="label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if End_RowCount==self.label_list[j][3]:
if End_RowCount==self.label_list[j][3] and End_ColCount>=self.label_list[j][5]:
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, End_RowCount, overlap_jobbar_count, 0, End_ColCount
self.label_list.append(label_pos)
k=k+1
n = n + 1
for label in self.label_list:
jobcount=label[0]
label_count=label[1]
label_name=label[2]
Start_Row=label[3]
overlap_jobbar_count=label[4]
Start_ColCount=label[5]
End_ColCount=label[6]
JobBarLeft=self.table.verticalHeader().width()+Start_ColCount*ColumnWidth
JobBarTop=self.table.horizontalHeader().height()+JobBarHeight*(overlap_jobbar_count-1)+Start_Row*RowHeight
JobBarWidth=(End_ColCount-Start_ColCount)*ColumnWidth
self.label_name=JobLabel(self.table)
self.label_name.setGeometry(JobBarLeft, JobBarTop,JobBarWidth, JobBarHeight)
self.label_name.setText(self.job_run_times[jobcount][0]+"-"+self.job_run_times[jobcount][1])
self.show()
def resizeEvent(self, event):
self.centralWidget.resize(event.size())
self.table.resize(self.centralWidget.size())
event.accept()
RowHeight=(self.table.size().height()-self.table.horizontalHeader().height())/7
ColumnWidth=(self.table.size().width() - self.table.verticalHeader().width())/24
JobBarHeight=20
for i in range(7):
self.table.setRowHeight(i,RowHeight)
for i in range(24):
self.table.setColumnWidth(i,ColumnWidth)
for label in self.label_list:
jobcount = label[0]
label_count = label[1]
label_name = label[2]
Start_Row = label[3]
overlap_jobbar_count = label[4]
Start_ColCount = label[5]
End_ColCount = label[6]
self.label_name.repaint()
self.label_name.parentWidget().repaint()
if __name__=="__main__":
app=QApplication(sys.argv)
win=window()
sys.exit(app.exec_())
它不起作用,因为您正在尝试设置 self.label_name
的几何形状,但只有一个对 JobLabel 的引用,它始终是 for
中创建的最后一个创建所有 JobLabel 实例的循环
(接近 initUI
的结尾)。
每次执行此操作时:
self.label_name=JobLabel(self.table)
JobLabel 已正确创建,但您正在丢失对前一个(如果有)的引用,因此将始终有一个可访问的 self.label_name
,尽管它们仍然存在于程序中(因为它已经取得了他们的所有权,所以他们不会 garbage-collected).
您应该做的是保留所有 JobLabel 的引用,相应地设置它们的数据并在调整大小时循环浏览它们。
class JobLabel(QLabel):
def __init__(self,parent, label_data):
QLabel.__init__(self,parent)
self.label_data = label_data
# ...
class window(QMainWindow):
# ...
def initUI(self):
# ...
self.label_list = []
self.label_widgets = []
# be careful, because you made an indentation error:
for job_run_time in self.job_run_times:
# ...
# self.label_list.append(label_pos)
# this for cycle should be aligned at the same line of the previous one,
# while you have put it inside it
for label in self.label_list:
label_widget = JobLabel(self.table, label)
# the following is unnecessary, as a resizeEvent will be sent before
# the window is shown the first time anyway
label_widget.setGeometry(JobBarLeft, JobBarTop,JobBarWidth, JobBarHeight)
label_widget.setText(self.job_run_times[jobcount][0]+"-"+self.job_run_times[jobcount][1])
self.label_widgets.append(label_widget)
def resizeEvent(self, event):
# ...
for label_widget in self.label_widgets:
label = label_widget.label_data
Start_Row = label[3]
overlap_jobbar_count = label[4]
Start_ColCount = label[5]
End_ColCount = label[6]
JobBarLeft=self.table.verticalHeader().width()+Start_ColCount*ColumnWidth
JobBarTop=self.table.horizontalHeader().height()+JobBarHeight*(overlap_jobbar_count-1)+Start_Row*RowHeight
JobBarWidth=(End_ColCount-Start_ColCount)*ColumnWidth
label_widget.setGeometry(JobBarLeft, JobBarTop,JobBarWidth, JobBarHeight)
也就是说,虽然您的 table 视图重叠的方法实际上是一个聪明的想法,但您的实施存在几个问题。
- 无需不断调整 header 部分的大小,只需对 header 和
都使用 setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
- 不要使用 table 宽度来计算列的位置(和宽度),因为它会给出不可靠的值:您正在使用 returns 浮点值的除法,设置每个 header 部分大小会导致整数大小;如果您使用上面写的拉伸模式,您可以获得从
self.table.horizontalHeader().sectionPosition(int(Start_ColCount))
开始的准确部分,并通过添加标签占用的列索引的所有 self.table.horizontalHeader().sectionSize(column)
来获得大小;垂直 size/position 也是如此
- 一旦行高 <(待办事项高度 * 并发待办事项数),使用固定大小的小部件就会出现问题,因为您会得到重叠或错误对齐的小部件;假设您将 table 的大小调整为单元格高度为 40 的点并且您有 3 个并发待办事项:最后一个 object 将与第二天对齐,如果那里已经有另一份工作,它可能会被隐藏
考虑到上面写的,我建议你更好的resizeEvent
实现:
def initUI(self):
# ...
# remove both for cycles of self.table.setColumnWidth and
# self.table.setRowHeight and replace them with this
self.table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.table.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
# ...
def resizeEvent(self, event):
# item views need some time to adjust their size (usually one "cycle" of
# the event loop), resulting in incoherent positioning. If the "oldSize"
# is invalid (a QSize with width or height < 0) we can assume that the
# window has not been shown yet, so we ignore the event and create a new
# one that will be "posted" afterwards, giving time to the table view to
# adjust its internal geometry and eventually get the correct sizes.
if not event.oldSize().isValid():
QApplication.postEvent(self, QtGui.QResizeEvent(event.size(), QtCore.QSize(1, 1)))
return
super(QMainWindow, self).resizeEvent(event)
hHeader = self.table.horizontalHeader()
vHeader = self.table.verticalHeader()
left = vHeader.width()
top = hHeader.height()
# adjust the job bar size if too small, otherwise keep the default 20px
JobBarHeight = min(20, vHeader.sectionSize(1) / 6)
for label_widget in self.label_widgets:
label = label_widget.label_data
Start_Row = label[3]
overlap_jobbar_count = label[4]
# I'm converting counts to integers as it's a requirement for range
# functions, and these values are actually float according to your
# implementation. You should make them as integers in the first place.
Start_ColCount = int(label[5])
End_ColCount = int(label[6])
vPos = vHeader.sectionPosition(Start_Row)
JobBarLeft = left + hHeader.sectionPosition(Start_ColCount)
JobBarTop = top + JobBarHeight*(overlap_jobbar_count-1) + vPos
JobBarWidth = sum(hHeader.sectionSize(s) for s in range(Start_ColCount, End_ColCount))
label_widget.setGeometry(JobBarLeft, JobBarTop,JobBarWidth, JobBarHeight)
我还建议您将 QDateTime 用于 "job" 计时,原因有二:
- 使用字符串来匹配事件日期并不总是一个好主意;虽然您可以对它们进行硬编码(正如您实际所做的那样),但如果有一天您决定切换到另一种格式(或者甚至使用垂直 headers 的本地化日期名称,因为您正在使用它们来匹配,这可能是一个问题日子);
- 很明显,您可以安排在一周内开始并在下周结束的活动;如果你不小心,你可能会在本周初看到事件,而实际上它们会在下周结束;
我假设您将使用某种基于字符串的序列化来保存和恢复事件数据,但这不是问题,因为您可以将 QDateTimes 转换为字符串并使用 QDateTime.toString
and QDateTime.fromString
using the QtCore.Qt.ISODate
参数返回以确保应用了正确的时区。
最后,虽然您的方法很有趣,但您需要小心,因为如果您需要更改每个 "event" 的 start/end 数据,它可能会更改其结果标签(如果事件导致的持续时间不会持续到接下来的几天,可能会删除一些最后的)。
我可能会创建一个 python class 来表示将保留(更新并最终 clear/add)其工作标签小部件的每个事件。在这种情况下,您不会使用 main self.label_widgets
来调整大小,但可能会循环遍历所有事件,然后循环遍历事件包含的每个 joblabel 小部件。
我正在编写一个待办事项列表应用程序以显示一周的每日(24 小时格式)时间表,这是我的设计:
- 在buttom层是一个在vboxlayout中使用QtableWidget的时间框架
- Qtablewidget 的重叠,我使用 QLabels 显示 schedule.Qlabels 未在任何布局中组织。
当主窗口调整大小时,由于vboxlayout,按钮层可以调整其大小。我想知道的是如何在调整按钮层大小时调整顶层大小。或任何其他方法来实现我的想法?我覆盖了 resizeEvent 但失败了。
import sys
from PyQt5 import QtWidgets,QtGui,QtCore
from PyQt5.QtWidgets import QApplication,QWidget,QFrame,QMainWindow,QLabel,QTableWidget,QVBoxLayout
from PyQt5.QtGui import QPainter,QFont,QBrush,QPen
from PyQt5.QtCore import Qt,QEvent
StyleSheet = '''
#central {
border: 0px;
background: gray;
}
QLabel {
border: 1px solid #C0C0C0;
background: #CCFF99;
font: 8pt Comic Sans MS;
border-radius: 4px;
}
#mainFrame {
border: 1px solid gray;
background: white;
}
QTableWidget {
background-color: white;
border: 0.5px solid #C0C0C0;
color: #F0F0F0;
gridline-color: #C0C0C0;
}
QHeaderView::section {
background-color: #FFD800;
padding: 0px;
font-size: 8pt;
border-style: none;
border-bottom: 1px solid #fffff8;
border-right: 1px solid #fffff8;
}
QHeaderView::section:horizontal
{
border-top: 1px solid #fffff8;
}
QHeaderView::section:vertical
{
border-left: 1px solid #fffff8;
font: 6pt Comic Sans MS;
}
QTableWidget::item::hover {
background-color: gray;
border: 0.5px solid #148CD2;
}
'''
class JobLabel(QLabel):
def __init__(self,parent):
QLabel.__init__(self,parent)
def enterEvent(self, event):
self.setStyleSheet("background-color: #99CCFF;")
def leaveEvent(self, event):
self.setStyleSheet("background-color: #CCFF99;")
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.left=100
self.top=100
self.width=1368
self.height=900
self.initUI()
def initUI(self):
self.setWindowTitle("Painting")
self.resize(1368,900)
self.setObjectName("mainWindow")
self.setStyleSheet(StyleSheet)
self.centralWidget=QWidget()
self.centralWidget.resize(self.width,self.height)
self.centralWidget.setObjectName("central")
mainlayout=QVBoxLayout()
ColumnCount=24
RowCount=7
JobBarHeight=20
self.table = QTableWidget(self.centralWidget)
self.table.move(0,0)
self.table.resize(self.centralWidget.width(),self.centralWidget.height())
self.table.setColumnCount(ColumnCount)
self.table.setRowCount(RowCount)
self.table.horizontalHeader().setVisible(True)
self.table.verticalHeader().setVisible(True)
self.table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
RowHeight=(self.table.size().height()-self.table.horizontalHeader().height())/RowCount
ColumnWidth=(self.table.size().width() - self.table.verticalHeader().width())/ColumnCount
hheaders = []
for i in range(1,ColumnCount+1):
if i<10:
hheaders.append("0{}:00".format(i))
else:
hheaders.append("{}:00".format(i))
self.table.setHorizontalHeaderLabels(hheaders)
for i in range(ColumnCount):
self.table.setColumnWidth(i, ColumnWidth)
vheaders = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
self.table.setVerticalHeaderLabels(vheaders)
for i in range(RowCount):
self.table.setRowHeight(i, RowHeight)
mainlayout.addWidget(self.table)
self.centralWidget.setLayout(mainlayout)
self.setCentralWidget(self.centralWidget)
self.job_run_times=[["Sun 12:00","Sun 14:35"],["Fri 22:00","Sat 4:35"],["Tue 1:00","Tue 6:00"],["Wed 17:00","Thu 19:00"],["Wed 18:00","Wed 21:30"],
["Mon 22:00","Mon 23:30"],["Sat 22:00","Mon 3:30"],["Sun 12:00","Sun 14:35"],["Fri 22:00","Sat 4:35"],["Mon 3:00","Mon 6:00"],
["Tue 1:00","Tue 6:00"],["Wed 17:00","Wed 19:30"],["Mon 17:00","Thu 19:00"],["Wed 18:00","Wed 21:30"],["Mon 4:00","Mon 7:00"],
["Mon 22:00","Mon 23:30"],["Sat 22:00","Mon 3:30"]]
#job count
n=0
#label count
k=0
self.label_list=[]
for job_run_time in self.job_run_times:
start=job_run_time[0]
end=job_run_time[1]
weekday_start=start.split()[0]
time_start=start.split()[1]
weekday_end=end.split()[0]
time_end=end.split()[1]
for i in range(RowCount):
if weekday_start==vheaders[i]:
Start_RowCount=i
if weekday_end==vheaders[i]:
End_RowCount=i
Start_ColCount=int(time_start.split(":")[0])+round(int(time_start.split(":")[1])/60,2)
End_ColCount=int(time_end.split(":")[0])+round(int(time_end.split(":")[1])/60,2)
#job time span multiple days
DaySpan=End_RowCount-Start_RowCount
if DaySpan==0:
label_name="label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if Start_RowCount==self.label_list[j][3]:
if (Start_ColCount>=self.label_list[j][5] and Start_ColCount<=self.label_list[j][6]) or (self.label_list[j][5]<=End_ColCount and End_ColCount<=self.label_list[j][6]):
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, Start_RowCount,overlap_jobbar_count,Start_ColCount, End_ColCount
self.label_list.append(label_pos)
k=k+1
n = n + 1
elif DaySpan>0:
label_name = "label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if Start_RowCount==self.label_list[j][3]:
if (Start_ColCount>=self.label_list[j][5] and Start_ColCount<=self.label_list[j][6]) :
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, Start_RowCount, overlap_jobbar_count, Start_ColCount, ColumnCount
self.label_list.append(label_pos)
k = k + 1
for i in range(DaySpan-1):
label_name="label_{}".format(k)
label_pos = []
overlap_jobbar_count = 1
for j in range(len(self.label_list)):
if Start_RowCount+i+1 == self.label_list[j][3]:
overlap_jobbar_count = overlap_jobbar_count + 1
label_pos = n, k, label_name, Start_RowCount + i + 1, overlap_jobbar_count, 0, ColumnCount
self.label_list.append(label_pos)
k=k+1
label_name="label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if End_RowCount==self.label_list[j][3] and End_ColCount>=self.label_list[j][5]:
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, End_RowCount,overlap_jobbar_count, 0, End_ColCount
self.label_list.append(label_pos)
k=k+1
n = n + 1
else:
label_name = "label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if Start_RowCount==self.label_list[j][3]:
if Start_ColCount>=self.label_list[j][5] and Start_ColCount<=self.label_list[j][6]:
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, Start_RowCount, overlap_jobbar_count, Start_ColCount, ColumnCount
self.label_list.append(label_pos)
k = k + 1
for i in range(6-Start_RowCount):
label_name="label_{}".format(k)
label_pos = []
overlap_jobbar_count = 1
for j in range(len(self.label_list)):
if Start_RowCount+i+1 == self.label_list[j][3]:
overlap_jobbar_count = overlap_jobbar_count + 1
label_pos = n, k, label_name, Start_RowCount + i + 1, overlap_jobbar_count, 0, ColumnCount
self.label_list.append(label_pos)
k=k+1
for i in range(End_RowCount):
label_name="label_{}".format(k)
label_pos = []
overlap_jobbar_count = 1
for j in range(len(self.label_list)):
if Start_RowCount+i + 1 == self.label_list[j][3]:
overlap_jobbar_count = overlap_jobbar_count + 1
label_pos = n, k, label_name, Start_RowCount + i + 1, overlap_jobbar_count, 0, ColumnCount
self.label_list.append(label_pos)
k=k+1
label_name="label_{}".format(k)
label_pos=[]
overlap_jobbar_count=1
for j in range(len(self.label_list)):
if End_RowCount==self.label_list[j][3]:
if End_RowCount==self.label_list[j][3] and End_ColCount>=self.label_list[j][5]:
overlap_jobbar_count=overlap_jobbar_count+1
label_pos = n, k, label_name, End_RowCount, overlap_jobbar_count, 0, End_ColCount
self.label_list.append(label_pos)
k=k+1
n = n + 1
for label in self.label_list:
jobcount=label[0]
label_count=label[1]
label_name=label[2]
Start_Row=label[3]
overlap_jobbar_count=label[4]
Start_ColCount=label[5]
End_ColCount=label[6]
JobBarLeft=self.table.verticalHeader().width()+Start_ColCount*ColumnWidth
JobBarTop=self.table.horizontalHeader().height()+JobBarHeight*(overlap_jobbar_count-1)+Start_Row*RowHeight
JobBarWidth=(End_ColCount-Start_ColCount)*ColumnWidth
self.label_name=JobLabel(self.table)
self.label_name.setGeometry(JobBarLeft, JobBarTop,JobBarWidth, JobBarHeight)
self.label_name.setText(self.job_run_times[jobcount][0]+"-"+self.job_run_times[jobcount][1])
self.show()
def resizeEvent(self, event):
self.centralWidget.resize(event.size())
self.table.resize(self.centralWidget.size())
event.accept()
RowHeight=(self.table.size().height()-self.table.horizontalHeader().height())/7
ColumnWidth=(self.table.size().width() - self.table.verticalHeader().width())/24
JobBarHeight=20
for i in range(7):
self.table.setRowHeight(i,RowHeight)
for i in range(24):
self.table.setColumnWidth(i,ColumnWidth)
for label in self.label_list:
jobcount = label[0]
label_count = label[1]
label_name = label[2]
Start_Row = label[3]
overlap_jobbar_count = label[4]
Start_ColCount = label[5]
End_ColCount = label[6]
self.label_name.repaint()
self.label_name.parentWidget().repaint()
if __name__=="__main__":
app=QApplication(sys.argv)
win=window()
sys.exit(app.exec_())
它不起作用,因为您正在尝试设置 self.label_name
的几何形状,但只有一个对 JobLabel 的引用,它始终是 for
中创建的最后一个创建所有 JobLabel 实例的循环
(接近 initUI
的结尾)。
每次执行此操作时:
self.label_name=JobLabel(self.table)
JobLabel 已正确创建,但您正在丢失对前一个(如果有)的引用,因此将始终有一个可访问的 self.label_name
,尽管它们仍然存在于程序中(因为它已经取得了他们的所有权,所以他们不会 garbage-collected).
您应该做的是保留所有 JobLabel 的引用,相应地设置它们的数据并在调整大小时循环浏览它们。
class JobLabel(QLabel):
def __init__(self,parent, label_data):
QLabel.__init__(self,parent)
self.label_data = label_data
# ...
class window(QMainWindow):
# ...
def initUI(self):
# ...
self.label_list = []
self.label_widgets = []
# be careful, because you made an indentation error:
for job_run_time in self.job_run_times:
# ...
# self.label_list.append(label_pos)
# this for cycle should be aligned at the same line of the previous one,
# while you have put it inside it
for label in self.label_list:
label_widget = JobLabel(self.table, label)
# the following is unnecessary, as a resizeEvent will be sent before
# the window is shown the first time anyway
label_widget.setGeometry(JobBarLeft, JobBarTop,JobBarWidth, JobBarHeight)
label_widget.setText(self.job_run_times[jobcount][0]+"-"+self.job_run_times[jobcount][1])
self.label_widgets.append(label_widget)
def resizeEvent(self, event):
# ...
for label_widget in self.label_widgets:
label = label_widget.label_data
Start_Row = label[3]
overlap_jobbar_count = label[4]
Start_ColCount = label[5]
End_ColCount = label[6]
JobBarLeft=self.table.verticalHeader().width()+Start_ColCount*ColumnWidth
JobBarTop=self.table.horizontalHeader().height()+JobBarHeight*(overlap_jobbar_count-1)+Start_Row*RowHeight
JobBarWidth=(End_ColCount-Start_ColCount)*ColumnWidth
label_widget.setGeometry(JobBarLeft, JobBarTop,JobBarWidth, JobBarHeight)
也就是说,虽然您的 table 视图重叠的方法实际上是一个聪明的想法,但您的实施存在几个问题。
- 无需不断调整 header 部分的大小,只需对 header 和 都使用
- 不要使用 table 宽度来计算列的位置(和宽度),因为它会给出不可靠的值:您正在使用 returns 浮点值的除法,设置每个 header 部分大小会导致整数大小;如果您使用上面写的拉伸模式,您可以获得从
self.table.horizontalHeader().sectionPosition(int(Start_ColCount))
开始的准确部分,并通过添加标签占用的列索引的所有self.table.horizontalHeader().sectionSize(column)
来获得大小;垂直 size/position 也是如此
- 一旦行高 <(待办事项高度 * 并发待办事项数),使用固定大小的小部件就会出现问题,因为您会得到重叠或错误对齐的小部件;假设您将 table 的大小调整为单元格高度为 40 的点并且您有 3 个并发待办事项:最后一个 object 将与第二天对齐,如果那里已经有另一份工作,它可能会被隐藏
setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
考虑到上面写的,我建议你更好的resizeEvent
实现:
def initUI(self):
# ...
# remove both for cycles of self.table.setColumnWidth and
# self.table.setRowHeight and replace them with this
self.table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.table.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
# ...
def resizeEvent(self, event):
# item views need some time to adjust their size (usually one "cycle" of
# the event loop), resulting in incoherent positioning. If the "oldSize"
# is invalid (a QSize with width or height < 0) we can assume that the
# window has not been shown yet, so we ignore the event and create a new
# one that will be "posted" afterwards, giving time to the table view to
# adjust its internal geometry and eventually get the correct sizes.
if not event.oldSize().isValid():
QApplication.postEvent(self, QtGui.QResizeEvent(event.size(), QtCore.QSize(1, 1)))
return
super(QMainWindow, self).resizeEvent(event)
hHeader = self.table.horizontalHeader()
vHeader = self.table.verticalHeader()
left = vHeader.width()
top = hHeader.height()
# adjust the job bar size if too small, otherwise keep the default 20px
JobBarHeight = min(20, vHeader.sectionSize(1) / 6)
for label_widget in self.label_widgets:
label = label_widget.label_data
Start_Row = label[3]
overlap_jobbar_count = label[4]
# I'm converting counts to integers as it's a requirement for range
# functions, and these values are actually float according to your
# implementation. You should make them as integers in the first place.
Start_ColCount = int(label[5])
End_ColCount = int(label[6])
vPos = vHeader.sectionPosition(Start_Row)
JobBarLeft = left + hHeader.sectionPosition(Start_ColCount)
JobBarTop = top + JobBarHeight*(overlap_jobbar_count-1) + vPos
JobBarWidth = sum(hHeader.sectionSize(s) for s in range(Start_ColCount, End_ColCount))
label_widget.setGeometry(JobBarLeft, JobBarTop,JobBarWidth, JobBarHeight)
我还建议您将 QDateTime 用于 "job" 计时,原因有二:
- 使用字符串来匹配事件日期并不总是一个好主意;虽然您可以对它们进行硬编码(正如您实际所做的那样),但如果有一天您决定切换到另一种格式(或者甚至使用垂直 headers 的本地化日期名称,因为您正在使用它们来匹配,这可能是一个问题日子);
- 很明显,您可以安排在一周内开始并在下周结束的活动;如果你不小心,你可能会在本周初看到事件,而实际上它们会在下周结束;
我假设您将使用某种基于字符串的序列化来保存和恢复事件数据,但这不是问题,因为您可以将 QDateTimes 转换为字符串并使用 QDateTime.toString
and QDateTime.fromString
using the QtCore.Qt.ISODate
参数返回以确保应用了正确的时区。
最后,虽然您的方法很有趣,但您需要小心,因为如果您需要更改每个 "event" 的 start/end 数据,它可能会更改其结果标签(如果事件导致的持续时间不会持续到接下来的几天,可能会删除一些最后的)。
我可能会创建一个 python class 来表示将保留(更新并最终 clear/add)其工作标签小部件的每个事件。在这种情况下,您不会使用 main self.label_widgets
来调整大小,但可能会循环遍历所有事件,然后循环遍历事件包含的每个 joblabel 小部件。