PyQt widget hide show crash Layout Problem

58 Views Asked by At

at start up Profile 0 hidden

Problem when profile 1 is hidden

While profile 1 is hidden try to hide 0 and then show 1 again

I have a nested layout 4-levels of nesting as in the attached photo

  • top layout is MainHorLayout |
  •     |--QVBoxLayout_______________ Hosts CheckBoxes
        |--AllfigVlayout_____________ Hosts figures
           |-TopHlayoutfig_0_1_______Hosts profile_0,Profile_1 charts, downside labels
           |   |                                                     
           |   |-- TopVlayoutFig_0_Lbl      (loaded with chart[0], chart[0].belowlabel)
           |   |
           |   |-- TopVlayoutFig_1_Lbl      (loaded with chart[1], chart[1].belowlabel)
           |              
           |-BotHlayoutfig_2_3_______same as TopHlayoutfig_0_1 but for bottom fig 2,3
               |                                                     
               |-- TopVlayoutFig_2_Lbl      (loaded with chart[2], chart[2].belowlabel)
               |
               |-- TopVlayoutFig_3_Lbl      (loaded with chart[2], chart[2].belowlabel)
    

I'm using check box to toggle the charts[i] visibale/ unvisible start up behaviour is shown in phot 1 No problem with Charts[0] and charts2 as in photo 2

The problem when i'm trying to hid charts1 it hides charts[0] and 1 also trying to hide charts3 it hides both 2 and 3 please check photo 3

when i trying to show them again the dimensions of charts is not equall and non symetrical please check photo 4 as it shows when program starts

from functools import partial
from scipy.interpolate import make_interp_spline, BSpline
import sys
import matplotlib.pyplot as plt
import numpy as np

#from PyQt5.QtWidgets import  QWidget
import matplotlib
matplotlib.use('QtAgg')
from PyQt6 import QtCore
from PyQt6.QtWidgets import QWidget,QApplication , QLabel, QSizePolicy, QFrame, QCheckBox, QVBoxLayout, QHBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from crosshairplt import CrossHairCursor
import mouse
import csv

NUMBERofProfiles = 4

class BelowFigLabel(QLabel):
    def __init__(self):
        super().__init__()
            
        sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
        #sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
           
        self.setMaximumHeight(60)
        #self.setMaximumHeight(self.height()/2)
        #sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)
        #self.setTextFormat(QtCore.Qt.TextFormat.RichText)
        self.setScaledContents(True)
        
        
        #self.label_1.setStyleSheet("font-family: Verdana,sans-serif;font: 87 10pt \"Arial Black\";\n""background-color: rgb(30,231,235);")#;font-size: 12px;height: 28px;width: 80px
        self.setStyleSheet("font-family: Verdana,sans-serif; font: 87 12pt ;font-weight:Bold;background-color: #007EA7;color:#E9D2F4")
                
        self.setFrameShape(QFrame.Shape.Box)
        self.setFrameShadow(QFrame.Shadow.Sunken)
        self.setLineWidth(2)
        


###############################
class Canvas(FigureCanvas):
    def __init__(self, parent,Horizontal,Vertical,figname,subscribt):
        
        self.parentx = parent
        self.t = Horizontal
        self.s = Vertical
        self.figname = figname +f"_{subscribt}"
        self.fig, self.ax = plt.subplots()      
        super().__init__(self.fig)
        
        self.setParent(parent)# parent must be a QWidget
        
        self.geo = self.parentx.geometry() # used to calculate the fig location
        
        self.x = (self.t[-1] - self.t[0])/2  #initial zome distance axis rang/2
        
        self.belowfiglbl = BelowFigLabel()
        
        self.colSize = self.s.shape[0] # get column size
        self.slic = [self.s[i][0] for i in  range(self.colSize)]
        
        #self.cursor = CrossHairCursor(self.ax,self.t,self.slic, self.belowfiglbl)

        self.ax.plot(self.t, self.s)
        self.ax.set(xlabel='time (s)', ylabel='Current (A))', title=self.figname)
        self.ax.grid()
        self.ax.grid(True,'both','x',color='r', linestyle='-' , linewidth=1)
        self.ax.grid(True,'both','y',color='b', linestyle='--', linewidth=1)
        self.ax.grid()
        ticks = []
        
        self.fig.canvas.mpl_connect('scroll_event', self.on_mouse_scroll)        
        #self.fig.canvas.mpl_connect('motion_notify_event',  self.cursor.on_mouse_move)
        #self.fig.canvas.mpl_connect('button_press_event',   self.cursor.on_mouse_click)
        #self.fig.canvas.mpl_connect('button_release_event', self.cursor.on_mouse_click)
        
        """ 
        Matplotlib Script
        """
        

    def on_mouse_scroll(self,event):       
        #self.geo = self.parentx.geometry()
        #print(self.geo)
        self.event = event
        if self.event.inaxes:
            if self.event.button == 'up':
                max_x =  self.event.xdata + self.x
                min_x =  self.event.xdata - self.x
                #print("P= %0.3f      min = %0.3f     max = %0.3f    x= %0.3f" %(self.event.xdata, min_x,max_x, self.x))
                if max_x > (min_x+.002): # ADD .002 MARGIN COZ when both limits get equall the widget behalves in WRONG WAY
                    if min_x > self.t[0] and max_x < self.t[-1]:
                        self.ax.set_xlim(min_x,max_x)
                        self.x /=2
                        #print("in bound")
                    elif min_x <= self.t[0]:
                        #print(" -------bound")
                        self.ax.set_xlim(self.t[0],max_x)
                        self.x /=2
                    else:
                        #print(" ++++++++++bound")
                        self.ax.set_xlim(min_x,self.t[-1])
                        self.x /=2
                   
            elif self.event.button == 'down':     
                x_0 = self.ax.get_xlim()
                #y_0 = self.ax.get_ylim()
                min_x = x_0[0]-self.x
                max_x = x_0[1]+self.x
                if min_x >= self.t[0] and max_x <= self.t[-1]:
                    self.ax.set_xlim(min_x,max_x)
                    self.x *=2
                elif min_x < self.t[0] and max_x <= self.t[-1]:
                    self.ax.set_xlim(self.t[0],max_x)
                    self.x *=2
                elif min_x>= self.t[0] and max_x > self.t[-1]:
                    self.ax.set_xlim(min_x,self.t[-1])
                    self.x *=2
                else:
                    self.ax.set_xlim(self.t[0],self.t[-1])
                    
            #self.fig.canvas.draw()
            """
               Get new Transformed co-ordinates after Canvas redraw to have updated xdata and ydata transformed
            
            """
            DataTransformedXtoCanvas,DataTransformedYtoCanvas = self.ax.transData.transform((self.event.xdata,self.event.ydata))
            
            """ 
                - The mouse object co-ordinates are the physical display
                - The event object Uses the Data Co-ordinates for event.xdata,event.ydata
                -  When we use the transData it tranform the data Co-ordinates into fig Co-ordinates
                
                - The Fig's are hosted in GridLayout so each figure has it on position relative to parent widget window
                - The parent window has its own relative position to physical dispaly
                
                ---- so to keep the mouse pointer pointing to the DATApOINT under zoom in/out
                     1- we first Transform Data Coordinates into fig coordinates using .transData.transform
                     2- add to the coordinates in (1) the canvas.geometry()'s topleft corner position relative to parent 
                        self.geometry().left(), self.geometry().top()
                     3- add the parent TopLeft corner position relative to PHYSICAL DISPLAY
            """
            CavasTopCornertoParent   = self.geometry().top()
            CavasLefCornertoParent   = self.geometry().left()
            ParentTopCornertoScreen  = self.parentx.geometry().top()
            ParentLeftCornertoScreen = self.parentx.geometry().left()
            PointerPositionX = (DataTransformedXtoCanvas + CavasLefCornertoParent + ParentLeftCornertoScreen)
            PointerPositionY = (DataTransformedYtoCanvas + CavasTopCornertoParent + ParentTopCornertoScreen)
            
            mouse.move(PointerPositionX , PointerPositionY)
            
    def on_mouse_click(self,event):
        print(event.xdata,event.ydata)
        print("geo = ", self.parentx.geometry())#fig.canvas.geometry())
        
        inverted = self.ax.transData.inverted().transform((event.x,event.y))
        print(f"inverted ={inverted},    screen xy ={(event.x,event.y)}")   
        
        
        
             
class AppDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(1600, 800)
        #self.setStyleSheet("font: 75 10pt \"Monospac821 BT\";""background-color: rgb(120,190,220);")
        self.setStyleSheet("font: 75 10pt Monospac821 BT;background-color: #003459")#rgb(120,190,220)")
        self.setWindowTitle("Current Oscilography")
        self.s=[]
        self.Ismoth=[]
        
        
        
        
        
        for i in range(NUMBERofProfiles):

            with open(f"E:/OneDrive/011_APROJECTS/digital relay/code/GitHub/Python Application/SourceCode/MODBUSfolder/loadprofiles/tstdata/LP_{i}.csv", mode='r', newline='') as profile:
                a = csv.reader(profile)
                x = 0
                CurrentTimeSamples = [] # list holding 5 col 4 for current and 1st is for time series
                for row in a:
                    try:
                        xx =[float(i) for i in row ]
                        CurrentTimeSamples.append(xx)
                    except:
                        CurrentTimeSamples.append(row)                           
                #CurrentTimeSamples is 5x801 list
                CurrentTimeSamples = CurrentTimeSamples[1:] # Remove Header Line
                CurrentOnly = [CurrentTimeSamples[i][1:] for i in  range(len(CurrentTimeSamples))] # remove 1st Col which is time series
                
                #Isolate the time series
                timex = np.array([CurrentTimeSamples[i][0] for i in  range(len(CurrentTimeSamples))])
                
                
                #self.s.append(CurrentOnly)
                #self.s[i]=np.array(self.s[i])
                # convert to Numpy 
                CurrentOnly = np.array(CurrentOnly)

                # evenly spaced 3200 points between Xmin and X
                self.time = np.linspace(timex.min(), timex.max(), 3200)
                # order 3 " Quadraic" Interpolation Equation
                spl = make_interp_spline(timex, CurrentOnly, k=3)
                # Application of the quadratic interpolation f
                self.Ismoth.append(spl(self.time))
            self.charts = []
        for i in range(NUMBERofProfiles):
            self.charts.append(Canvas(self,self.time,self.Ismoth[i],"Profile", i))
    
        self.MainHorLayout = QHBoxLayout(self)
        #self.MainHorLayout.setSpacing(0)
        
        self.TopHlayoutfig_0_1 = QHBoxLayout()
        #self.TopHlayoutfig_0_1.setSpacing(0)
        
        self.TopHlayoutfig_0_1.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)#QtCore.Qt.AlignmentFlag.AlignLeft|

        
        self.TopVlayoutFig_0_Lbl =QVBoxLayout()
        self.TopVlayoutFig_0_Lbl.addWidget(self.charts[0])
        self.TopVlayoutFig_0_Lbl.addWidget(self.charts[0].belowfiglbl)
        
        self.TopVlayoutFig_1_Lbl =QVBoxLayout()
        self.TopVlayoutFig_1_Lbl.addWidget(self.charts[1])
        self.TopVlayoutFig_1_Lbl.addWidget(self.charts[1].belowfiglbl)
        
        
        self.TopHlayoutfig_0_1.addLayout(self.TopVlayoutFig_0_Lbl)
        self.TopHlayoutfig_0_1.addLayout(self.TopVlayoutFig_1_Lbl)
        
        
        self.BotHlayoutfig_2_3 = QHBoxLayout()
        self.BotHlayoutfig_2_3.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
        
        self.BotVlayoutFig_2_Lbl =QVBoxLayout()
        self.BotVlayoutFig_2_Lbl.addWidget(self.charts[2])
        self.BotVlayoutFig_2_Lbl.addWidget(self.charts[2].belowfiglbl)
             
        self.BotVlayoutFig_3_Lbl =QVBoxLayout()
        self.BotVlayoutFig_3_Lbl.addWidget(self.charts[3])
        self.BotVlayoutFig_3_Lbl.addWidget(self.charts[3].belowfiglbl)
             
        self.BotHlayoutfig_2_3.addLayout(self.BotVlayoutFig_2_Lbl)
        self.BotHlayoutfig_2_3.addLayout(self.BotVlayoutFig_3_Lbl)
        
        self.AllfigVlayout = QVBoxLayout()
        
        self.AllfigVlayout.addLayout(self.TopHlayoutfig_0_1)
        self.AllfigVlayout.addLayout(self.BotHlayoutfig_2_3)
        #self.AllfigVlayout.setSpacing(0)
        self.MainHorLayout.addLayout(self.AllfigVlayout)
        
       
        SideBarVLayout_main = QVBoxLayout()   
        
        
        LeftSidbarLabel = QLabel()
        
        ChBoxesFrame = QFrame()
        ChBoxesFrame.setStyleSheet("font-family: Verdana,sans-serif; font: 40 12pt ;font-weight:Bold;background-color: #30638E;color:#ffd166")
        ChBoxesFrame.setContentsMargins(5,1,5,5)
        ChBoxesFrame.setFrameShape(QFrame.Shape.Box)
        ChBoxesFrame.setFrameShadow(QFrame.Shadow.Sunken)
        ChBoxesFrame.setLineWidth(3)
        ChBoxesFrame.setMaximumHeight(220)

        ChBoxesFrame_Vlaouy = QVBoxLayout(ChBoxesFrame)
        
        SelectActvProfilLabel = QLabel("SeLect Profiles", ChBoxesFrame)
        SelectActvProfilLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop|QtCore.Qt.AlignmentFlag.AlignLeft)
        SelectActvProfilLabel.setMaximumHeight(20)
        
        ChBoxesFrame_Vlaouy.addWidget(SelectActvProfilLabel)
        cc = QCheckBox()
        
        self.ChkBox =[]
        for i in range(NUMBERofProfiles):
            self.ChkBox.append(QCheckBox(f"Profile_{i}",ChBoxesFrame))
            self.ChkBox[i].setChecked(True)
            self.ChkBox[i].clicked.connect(partial(self.chkBox_cliked,i))
            ChBoxesFrame_Vlaouy.addWidget(self.ChkBox[i])
            
        SideBarVLayout_main.addWidget(ChBoxesFrame)
        
        LeftSidbarLabel.setMaximumWidth(180)
        LeftSidbarLabel.setStyleSheet("font-family: Verdana,sans-serif; font: 40 12pt ;font-weight:Bold;background-color: #30638E;color:#EDAE49")
        LeftSidbarLabel.setContentsMargins(10,5,5,5)
        LeftSidbarLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop|QtCore.Qt.AlignmentFlag.AlignLeft)
        LeftSidbarLabel.setFrameShape(QFrame.Shape.Box)
        LeftSidbarLabel.setFrameShadow(QFrame.Shadow.Sunken)
        LeftSidbarLabel.setLineWidth(3)        
        
        SideBarVLayout_main.addWidget(LeftSidbarLabel)
        
        
                
        self.MainHorLayout.addLayout(SideBarVLayout_main, )
        
    def chkBox_cliked(self,ckboxNum):        
        #for i in range(NUMBERofProfiles):
        if self.ChkBox[ckboxNum].isChecked() == False:
            
            self.charts[ckboxNum].belowfiglbl.setVisible(False)
            #deleteLater()
            self.charts[ckboxNum].setVisible(False)
            
        else:
            
            self.charts[ckboxNum].setVisible(True)# = Canvas(self,self.time,self.Ismoth[ckboxNum],"Profile",ckboxNum)
            self.charts[ckboxNum].belowfiglbl.setVisible(True)
        #self.MainHorLayout.update()  
        
            
               
if __name__ == "__main__":
    
    app = QApplication(sys.argv)        
    demo = AppDemo()
    demo.show()#Maximized()
    sys.exit(app.exec())
0

There are 0 best solutions below