0

I'm a beginner and I know there is something I missed but I don't know exactly what, So I have a PySide6 app, and I created a function to generate a calendar in a QTableWidget using calendar module in python all worked fine but the problem came when I tried to add a navigation buttons to get next and previous month: This is my function:

import sys
import os
import platform
import datetime as dt
import time
import calendar

from PySide6 import *
from PySide6 import QtGui
from PySide6 import QtWidgets
from PySide6 import QtCore
from PySide6.QtGui import QColor

from functools import partial

yy = int(dt.datetime.now().strftime("%Y"))
mm = int(dt.datetime.now().strftime("%m"))

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        global widgets
        widgets = self.ui

        # Calender generator
        self.calender_gen(mm,yy)



    def calender_gen(self, mm_g, yy_g):
        # Creat table rows and columns
        widgets.tableWidget_3.setRowCount(5)
        widgets.tableWidget_3.setColumnCount(7)

        # Table header labels
        week_list = ["Sat","Sun","Mon","Tue","Wed","Thu","Fri"]
        widgets.tableWidget_3.setHorizontalHeaderLabels(week_list)

        # Start inserting days of the month into the table
        row = 0
        col = 0
        for week in calendar.monthcalendar(yy_g,mm_g):
            for day in week:
                if day == 0:
                    widgets.tableWidget_3.setItem(row,col,QTableWidgetItem(" "))
                else:
                    widgets.tableWidget_3.setItem(row,col,QTableWidgetItem(str(day)))
                col += 1
            row += 1
            col = 0
        print(mm_g,yy_g)
        # Connect Buttons to function
        widgets.pushButton_3.clicked.connect(partial(self.next_calendar_butt,mm_g,yy_g))
        widgets.pushButton_2.clicked.connect(partial(self.prev_calendar_butt,mm_g,yy_g))

    def next_calendar_butt(self,mm_new, yy_new):
        mm_new += 1
        if mm_new > 12:
            mm_new = 1
            yy_new += 1

        widgets.tableWidget_3.setRowCount(0)
        widgets.tableWidget_3.setColumnCount(0)
        self.calender_gen(mm_new,yy_new)

    def prev_calendar_butt(self,mm_g_new,yy_g_new):
        mm_g_new -= 1
        if mm_g_new == 0:
            mm_g_new = 12
            yy_g_new -= 1

        widgets.tableWidget_3.setRowCount(0)
        widgets.tableWidget_3.setColumnCount(0)
        self.calender_gen(mm_g_new,yy_g_new)

When I run the app the calendar shows in the table as in the image image of the GUI table

The Console output Console prints 11 2021

When I click on pushButton_3 for first time click it works normally and prints '12 2021' in the console console prints 12 2021 But after I click on the same button again it starts duplication: console prints '12 2021 1 2022' console prints 12 2021 1 2022 If I click again it prints '12 2021 1 2022 1 2022 2 2022' as in the image enter image description here with every click it duplicates more where it should only print one statement i.e '2 2022'

I tried to move the below lines out of the calendar_gen() function but I coudn't deliver the parameters, even after declaring global variables and assign them to the parameters :

widgets.pushButton_3.clicked.connect(partial(self.next_calendar_butt,mm_g,yy_g))
widgets.pushButton_2.clicked.connect(partial(self.prev_calendar_butt,mm_g,yy_g))

I've tried to do this: Inside the calendar_gen() function I declared global variables and assigned them to the function parameters in order to create something like a global parameter

global var_mm
global var_yy

var_mm = mm_g
var_yy = yy_g

then in the init(self) function I put those 2 lines:

def __init__(self):
    widgets.pushButton_3.clicked.connect(partial(self.next_calendar_butt,var_mm,var_yy))
    widgets.pushButton_2.clicked.connect(partial(self.prev_calendar_butt,var_mm,var_yy))

But this didn't work in the console it prints '11 2021' when I run the app then when I click on pushButton_3 it prints '12 2021' and when I click again on it, it prints '12 2021' again and so on Same with the other button it prints '10 2021' again and again

  • Please [edit] your question in order to provide a [mre] and check that you're properly [formatting code](https://meta.stackoverflow.com/a/251362), as right now it's full of indentation problems that makes it really difficult (and annoying) to fix it. – musicamante Nov 21 '21 at 22:49
  • Really sorry for that, I edited the code – Hosam Hashem Nov 21 '21 at 23:10
  • Please consider that while your update improved the code, it is still *not* reproducible, as the UI is not provided and the imports are incomplete (I can *suppose* that `calendar` is from the standard library, but people cannot be completely sure about that, and we're forced to guess). That said, is there a reason for not using [QCalendarWidget](https://doc.qt.io/qt-5/qcalendarwidget.html)? – musicamante Nov 21 '21 at 23:39
  • I want to implement some functionalities that I can't do with QCalendarWidget, for example, specific days from the month will be highlighted, and implement some action triggers when I navigate between months. With QCalenderWidget that will be very hard to do (for example QcalenderWidgets doesn't support a function to tell if navigation button was pressed) – Hosam Hashem Nov 21 '21 at 23:49
  • That's not entirely true: [`paintCell()`](https://doc.qt.io/qt-5/qcalendarwidget.html#paintCell) provides partial overriding of a painting of a specific date, and [this answer](https://stackoverflow.com/a/57068146) shows that's it's possible to access navigation buttons (but nothing prevents you to use QCalendarWidget without buttons and add your own). Implementing a *proper* calendar widget using a QTableWidget is doable, but you have to consider that it's not easy, nor obvious: there's a reason for which the source code of QCalendarWidget is more than 3000 lines of code. – musicamante Nov 21 '21 at 23:54
  • Be aware that you are most certainly facing an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem): asking a solution for the wrong problem. For future reference, I suggest you to add some (brief but specific) insight on what you're trying to achieve, as it's always possible that you're trying to find a solution for a problem that might not even exist. – musicamante Nov 21 '21 at 23:55

1 Answers1

0

Qt signal connections are not exclusive (by default), and a signal can be connected to the same function more than once.

Since you're connecting the clicked signals of the buttons in calender_gen, everytime that function is called you're adding a further connection to those signals. The result is that the connected functions will be called as many time as they have been connected every time the signal is emitted.

A proper solution is to connect to the functions that would switch the month and keep a reference to the current month for the "new" month computation.

Since the functions are almost identical, it's better to group them in a unique function, and then connect the signals to separate functions that would eventually call that former function with an appropriate parameter:

class MainWindow(QMainWindow):
    def __init__(self):
        # ...
        self.calender_gen(mm,yy)

        self.pushButton_2.clicked.connect(self.prev_month)
        self.pushButton_3.clicked.connect(self.next_month)

    def calender_gen(self, mm_g, yy_g):
        # Creat table rows and columns
        self.tableWidget_3.setRowCount(5)
        self.tableWidget_3.setColumnCount(7)

        # Table header labels
        week_list = ["Sat","Sun","Mon","Tue","Wed","Thu","Fri"]
        self.tableWidget_3.setHorizontalHeaderLabels(week_list)

        # Start inserting days of the month into the table
        row = 0
        col = 0
        for week in calendar.monthcalendar(yy_g,mm_g):
            for day in week:
                if day == 0:
                    self.tableWidget_3.setItem(row,col,QTableWidgetItem(" "))
                else:
                    self.tableWidget_3.setItem(row,col,QTableWidgetItem(str(day)))
                col += 1
            row += 1
            col = 0

        self.current_month = mm_g
        self.current_year = yy_g

    def prev_month(self):
        self.step_month(-1)

    def next_month(self):
        self.step_month(1)

    def step_month(self, delta):
        mm_new = self.current_month + delta
        mm_year = self.current_year
        if mm_new > 12:
            mm_new = 1
            mm_year += 1
        elif mm_new < 1:
            mm_new = 12
            mm_year -= 1

        self.calender_gen(mm_new, mm_year)

Obviously, properly implementing QCalendarWidget might be much simpler, as it already provides most of the functionalities.

musicamante
  • 41,230
  • 6
  • 33
  • 58