我正在编写待办事项清单应用程序,以显示一周的每日(24H格式)时间表,这是我的设计:
主窗口调整大小时,由于vboxlayout,buttom层可以调整其大小。我想知道的是调整buttom层的大小时如何调整顶层的大小。或其他任何方法来实现我的想法?我覆盖了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的引用,该引用始终是在创建所有JobLabel实例的for
循环中创建的最后一个引用(在initUI
的结尾附近)。
每次您这样做:
self.label_name=JobLabel(self.table)
JobLabel已正确创建,但是您丢失了对前一个的引用(如果有的话),因此,尽管程序中仍然存在单个可访问的self.label_name
,但它们始终存在(因为它已经占用了它们的所有权,因此不会被垃圾收集)。
您应该做的是保留所有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)
也就是说,虽然您使用表视图重叠的方法实际上是一个聪明的主意,但是您的实现存在一些问题。
setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.table.horizontalHeader().sectionPosition(int(Start_ColCount))
相加来获得从self.table.horizontalHeader().sectionSize(column)
和尺寸开始的准确部分;垂直尺寸/位置也是如此考虑以上内容,我建议您使用更好的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进行“作业”计时:
我假设您将使用某种基于字符串的序列化来保存和还原事件数据,但这不是问题,因为您可以使用QDateTime.toString
和QDateTime.toString
将QDateTimes转换为字符串,然后使用QDateTime.fromString
参数确保应用了正确的时区。
最后,虽然您的方法很有趣,但是您需要注意这一点,因为如果您需要更改每个“事件”的开始/结束数据,则可能会更改其结果标签(可能是通过删除一些如果事件导致的持续时间不会持续到接下来的几天,则为最后一个)。我可能会创建一个表示每个事件的python类,该事件将保留(更新并最终清除/添加)其joblabel小部件。在这种情况下,您将不会使用主QDateTime.fromString
来调整大小,但可能会循环浏览所有事件,然后循环显示该事件包含的每个joblabel小部件。