How to have two widgets in one Main window - python

I am trying the whole morning already to fix that.
So I have a PyQt Main Window where I want to display two widgets.
In the first widget there are articles listed (which works so far).
When I click on them until now a QMessageBox is opening, but I want that
a second widget is opening where I can read the RSS Feed.
But this is not working. See Code below:
class ArticleWidgets(QWidget):
def __init__(self, *args):
super().__init__(*args)
self.setGeometry(610, 610, 600, 600)
self.initUi()
def initUi(self):
self.box = QHBoxLayout(self)
def show(self, feed=None):
self.title = QLabel()
self.summary = QLabel()
self.link = QLabel()
if feed:
self.title.setText(feed[0])
self.summary.setText(feed[1])
self.link.setText(feed[2])
self.box.addWidget(self.title)
self.box.addWidget(self.summary)
self.box.addWidget(self.link)
self.setLayout(self.box)
class TitleWidgets(QWidget):
def __init__(self, *args):
super().__init__(*args)
self.setGeometry(10, 10, 600, 600)
self.initUi()
def initUi(self):
vbox = QHBoxLayout(self)
self.titleList = QListWidget()
self.titleList.itemDoubleClicked.connect(self.onClicked)
self.titleList.setGeometry(0, 0, 400, 400)
self.news = ANFFeed()
for item in self.news.all_feeds:
self.titleList.addItem(item[0])
vbox.addWidget(self.titleList)
def onClicked(self, item):
feeds = self.news.all_feeds
id = 0
for elem in range(len(feeds)):
if feeds[elem][0] == item.text():
id = elem
summary = feeds[id][1] + '\n\n'
link = feeds[id][2]
if feeds and id:
#ANFApp(self).show_articles(feeds[id])
show = ANFApp()
show.show_articles(feed=feeds[id])
QMessageBox.information(self, 'Details', summary + link)
class ANFApp(QMainWindow):
def __init__(self, *args):
super().__init__(*args)
self.setWindowState(Qt.WindowMaximized)
self.setWindowIcon(QIcon('anf.png'))
self.setAutoFillBackground(True)
self.anfInit()
self.show()
def anfInit(self):
self.setWindowTitle('ANF RSS Reader')
TitleWidgets(self)
#article_box = ArticleWidgets(self)
exitBtn = QPushButton(self)
exitBtn.setGeometry(600, 600, 100, 50)
exitBtn.setText('Exit')
exitBtn.setStyleSheet("background-color: red")
exitBtn.clicked.connect(self.exit)
def show_articles(self, feed=None):
present = ArticleWidgets()
present.show(feed)
def exit(self):
QCoreApplication.instance().quit()

Solution using Pyqtgraph's Docks and QTextBrowser
Here is a code trying to reproduce your sketch. I used the Pyqtgraph module (Documentation here: Pyqtgraph's Documentation and Pyqtgraph's Web Page) because its Dock widget is easier to use and implement from my perspective.
You must install the pyqtgraph module before trying this code:
import sys
from PyQt5 import QtGui, QtCore
from pyqtgraph.dockarea import *
class DockArea(DockArea):
## This is to prevent the Dock from being resized to te point of disappear
def makeContainer(self, typ):
new = super(DockArea, self).makeContainer(typ)
new.setChildrenCollapsible(False)
return new
class MyApp(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
central_widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
label = QtGui.QLabel('This is a label, The widgets will be below')
label.setMaximumHeight(15)
## The DockArea as its name says, is the are where we place the Docks
dock_area = DockArea(self)
## Create the Docks and change some esthetic of them
self.dock1 = Dock('Widget 1', size=(300, 500))
self.dock2 = Dock('Widget 2', size=(400, 500))
self.dock1.hideTitleBar()
self.dock2.hideTitleBar()
self.dock1.nStyle = """
Dock > QWidget {
border: 0px solid #000;
border-radius: 0px;
}"""
self.dock2.nStyle = """
Dock > QWidget {
border: 0px solid #000;
border-radius: 0px;
}"""
self.button = QtGui.QPushButton('Exit')
self.widget_one = WidgetOne()
self.widget_two = WidgetTwo()
## Place the Docks inside the DockArea
dock_area.addDock(self.dock1)
dock_area.addDock(self.dock2, 'right', self.dock1)
## The statment above means that dock2 will be placed at the right of dock 1
layout.addWidget(label)
layout.addWidget(dock_area)
layout.addWidget(self.button)
## Add the Widgets inside each dock
self.dock1.addWidget(self.widget_one)
self.dock2.addWidget(self.widget_two)
## This is for set the initial size and posotion of the main window
self.setGeometry(100, 100, 600, 400)
## Connect the actions to functions, there is a default function called close()
self.widget_one.TitleClicked.connect(self.dob_click)
self.button.clicked.connect(self.close)
def dob_click(self, feed):
self.widget_two.text_box.clear()
## May look messy but wat i am doing is somethin like this:
## 'Title : ' + feed[0] + '\n\n' + 'Summary : ' + feed[1]
self.widget_two.text_box.setText(
'Title : ' + feed[0]\
+ '\n\n' +\
'Summary : ' + feed[1]
)
class WidgetOne(QtGui.QWidget):
## This signal is created to pass a "list" when it (the signal) is emited
TitleClicked = QtCore.pyqtSignal([list])
def __init__(self):
QtGui.QWidget.__init__(self)
self.layout = QtGui.QVBoxLayout()
self.setLayout(self.layout)
self.titleList = QtGui.QListWidget()
self.label = QtGui.QLabel('Here is my list:')
self.layout.addWidget(self.label)
self.layout.addWidget(self.titleList)
self.titleList.addItem(QtGui.QListWidgetItem('Title 1'))
self.titleList.addItem(QtGui.QListWidgetItem('Title 2'))
self.titleList.itemDoubleClicked.connect(self.onClicked)
def onClicked(self, item):
## Just test values
title = item.text()
summary = "Here you will put the summary of {}. ".format(title)*50
## Pass the values as a list in the signal. You can pass as much values
## as you want, remember that all of them have to be inside one list
self.TitleClicked.emit([title, summary])
class WidgetTwo(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.layout = QtGui.QVBoxLayout()
self.setLayout(self.layout)
self.label2 = QtGui.QLabel('Here we show results?:')
self.text_box = QtGui.QTextBrowser()
self.layout.addWidget(self.label2)
self.layout.addWidget(self.text_box)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Again, there are comments inside the code to help you understand what I did.
Here is how it looks:
If you pass the mouse between the two widgets you will see the mouse icon will change, with that you can readjust on the run the size of both widgets.
Final Words
This is another approach, more "interactive" and more esthetic than my previous answer. As you said, using a QSplitter works too.

Problems
The way you are building your GUI is, in my opinion, messy and it may lead to errors. I suggest the use of Layouts for a more organized GUI.
The other problem is that each widget is an independent class so if you want to connect an action in one widget to do something in the other widget through the Main Window, you must use Signals.
Edit : Another suggestion, use other name for the close function instead of exit and try using self.close() instead of QCoreApplication.instance().quit()
Solution
Trying to emulate what you want to do I made this GUI:
import sys
from PyQt5 import QtGui, QtCore
class MyWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
## Generate the structure parts of the MainWindow
self.central_widget = QtGui.QWidget() # A QWidget to work as Central Widget
self.layout1 = QtGui.QVBoxLayout() # Vertical Layout
self.layout2 = QtGui.QHBoxLayout() # Horizontal Layout
self.widget_one = WidgetOne()
self.widget_two = WidgetTwo()
self.exitBtn = QtGui.QPushButton('Exit')
## Build the structure
# Insert a QWidget as a central widget for the MainWindow
self.setCentralWidget(self.central_widget)
# Add a principal layout for the widgets/layouts you want to add
self.central_widget.setLayout(self.layout1)
# Add widgets/layuts, as many as you want, remember they are in a Vertical
# layout: they will be added one below of the other
self.layout1.addLayout(self.layout2)
self.layout1.addWidget(self.exitBtn)
# Here we add the widgets to the horizontal layout: one next to the other
self.layout2.addWidget(self.widget_one)
self.layout2.addWidget(self.widget_two)
## Connect the signal
self.widget_one.TitleClicked.connect(self.dob_click)
def dob_click(self, feed):
## Change the properties of the elements in the second widget
self.widget_two.title.setText('Title : '+feed[0])
self.widget_two.summary.setText('Summary : '+feed[1])
## Build your widgets same as the Main Window, with the excepton that here you don't
## need a central widget, because it is already a widget.
class WidgetOne(QtGui.QWidget):
TitleClicked = QtCore.pyqtSignal([list]) # Signal Created
def __init__(self):
QtGui.QWidget.__init__(self)
##
self.layout = QtGui.QVBoxLayout() # Vertical Layout
self.setLayout(self.layout)
self.titleList = QtGui.QListWidget()
self.label = QtGui.QLabel('Here is my list:')
self.layout.addWidget(self.label)
self.layout.addWidget(self.titleList)
self.titleList.addItem(QtGui.QListWidgetItem('Title 1'))
self.titleList.addItem(QtGui.QListWidgetItem('Title 2'))
self.titleList.itemDoubleClicked.connect(self.onClicked)
def onClicked(self, item):
## Just test parameters and signal emited
self.TitleClicked.emit([item.text(), item.text()+item.text()])
class WidgetTwo(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.layout = QtGui.QVBoxLayout()
self.setLayout(self.layout)
self.title = QtGui.QLabel('Title : ---')
self.summary = QtGui.QLabel('Summary : ---')
self.link = QtGui.QLabel('Link : ---')
self.layout.addWidget(self.title)
self.layout.addWidget(self.summary)
self.layout.addWidget(self.link)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
Inside the code, there are comments to help you understand why I did to build an organized GUI. There is also an example of a Signal being used to connect the action of itemDoubleClicked from the first widget to the second one. Here is how the MainWindow looks:
It is not very clear how the layouts work just from seeing the result, so I did a little paint over to a better understanding:
The blue box is the vertical layout (QVBoxLayout) and the red one is the horizontal layout (QHBoxLayout). Inside the blue layout, are located the red layout (above) and the exit button (below); and inside the red layout, are located the widget_1 (left) and the widget_2 (right).
Other Solution
An "easier" solution will be building the widgets inside the MainWindow instead of creating separate classes. With this you will avoid the use of signals, but the code will become a little more confusing because all the code will be cramped in one class.

Related

PyQt5 removing button

In the list_widget I have added a add button I also want to add a remove button which asks which item you wants to remove and remove the chosen item. I was trying it to do but I didn't had any idea to do so .Also, please explain the solution I am a beginner with pyqt5 or I'd like to say absolute beginner.
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication,QMainWindow,
QListWidget, QListWidgetItem
import sys
class MyWindow(QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.x = 200
self.y = 200
self.width = 500
self.length = 500
self.setGeometry(self.x, self.y, self.width,
self.length)
self.setWindowTitle("Stock managment")
self.iniTUI()
def iniTUI(self):
# buttons
self.b1 = QtWidgets.QPushButton(self)
self.b1.setText("+")
self.b1.move(450, 100)
self.b1.resize(50, 25)
self.b1.clicked.connect(self.take_inputs)
# This is the button I want to define.
self.btn_minus = QtWidgets.QPushButton(self)
self.btn_minus.setText("-")
self.btn_minus.move(0, 100)
self.btn_minus.resize(50, 25)
# list
self.list_widget = QListWidget(self)
self.list_widget.setGeometry(120, 100, 250, 300)
self.item1 = QListWidgetItem("A")
self.item2 = QListWidgetItem("B")
self.item3 = QListWidgetItem("C")
self.list_widget.addItem(self.item1)
self.list_widget.addItem(self.item2)
self.list_widget.addItem(self.item3)
self.list_widget.setCurrentItem(self.item2)
def take_inputs(self):
self.name, self.done1 =
QtWidgets.QInputDialog.getText(
self, 'Add Item to List', 'Enter The Item you want
in
the list:')
self.roll, self.done2 = QtWidgets.QInputDialog.getInt(
self, f'Quantity of {str(self.name)}', f'Enter
Quantity of {str(self.name)}:')
if self.done1 and self.done2:
self.item4 = QListWidgetItem(f"{str(self.name)}
Quantity{self.roll}")
self.list_widget.addItem(self.item4)
self.list_widget.setCurrentItem(self.item4)
def clicked(self):
self.label.setText("You clicked the button")
self.update()
def update(self):
self.label.adjustSize()
def clicked():
print("meow")
def window():
apk = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(apk.exec_())
window()
The core issue here is the lack of separation of the view and the data. This makes it very hard to reason about how to work with graphical elements. You will almost certainly want to follow the Model View Controller design paradigm https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
which offers a systematic way to handle this separation.
Once you do so, it immediately becomes very straight forward how to proceed with the question: You essentially just have a list, and you either want to add a thing to this list, or remove one based on a selection.
I include an example here which happens to use the built-in classes QStringListModel and QListView in Qt5, but it is simple to write your own more specialized widgets and models. They all just use a simple signal to emit to the view that it needs to refresh the active information.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class StuffViewer(QMainWindow):
def __init__(self, model):
super().__init__()
self.setWindowTitle("Stock managment")
# 1: Use layouts.
hbox = QHBoxLayout()
widget = QWidget()
widget.setLayout(hbox)
self.setCentralWidget(widget)
# 2: Don't needlessly store things in "self"
vbox = QVBoxLayout()
add = QPushButton("+")
add.clicked.connect(self.add_new_stuff)
vbox.addWidget(add)
sub = QPushButton("-")
sub.clicked.connect(self.remove_selected_stuff)
vbox.addWidget(sub)
vbox.addStretch(1)
hbox.addLayout(vbox)
# 3: Separate the view of the data from the data itself. Use Model-View-Controller design to achieve this.
self.model = model
self.stuffview = QListView()
self.stuffview.setModel(self.model)
hbox.addWidget(self.stuffview)
def add_new_stuff(self):
new_stuff, success = QInputDialog.getText(self, 'Add stuff', 'Enter new stuff you want')
if success:
self.stuff.setStringList(self.stuff.stringList() + [new_stuff])
def remove_selected_stuff(self):
index = self.stuffview.currentIndex()
all_stuff = self.stuff.stringList()
del all_stuff[index.column()]
self.stuff.setStringList(all_stuff)
def window():
apk = QApplication(sys.argv)
# Data is clearly separated:
# 4: Never enumerate variables! Use lists!
stuff = QStringListModel(["Foo", "Bar", "Baz"])
# The graphical components is just how you interface with the data with the user!
win = StuffViewer(stuff)
win.show()
sys.exit(apk.exec_())
window()

Using 3 Buttons in PyQt5 DialogButtonBox

I'm attempting to create a Login System type dialog box for practice using PyQt5 (I'm quite new to the module) and i'm trying to give the user the ability to click (Ok, Cancel, Apply) as the buttons underneath inputs boxes for Username / Password, but i'm not sure how I can actually get the apply button to work. I have buttons.accepted.connect(*method*) and buttons.rejected.connect(*method*) but I don't know how to specify the pressing of the accept button. I have tried using buttons.clicked(dlgButtons[0] (Which is where the button is stored) but it just gives me an error.
The code below is my declaration of the buttons if that helps. Thanks
buttons = qt.QDialogButtonBox()
dlgButtons = (qt.QDialogButtonBox.Apply, qt.QDialogButtonBox.Ok, qt.QDialogButtonBox.Cancel)
buttons.setStandardButtons(
dlgButtons[0] | dlgButtons[1] | dlgButtons[2]
)
One possible solution might look like this:
from PyQt5.QtWidgets import *
class ModelessDialog(QDialog):
def __init__(self, part, threshold, parent=None):
super().__init__(parent)
self.setWindowTitle("Baseline")
self.setGeometry(800, 275, 300, 200)
self.part = part
self.threshold = threshold
self.threshNew = 4.4
label = QLabel("Part : {}\nThreshold : {}".format(
self.part, self.threshold))
self.label2 = QLabel("ThreshNew : {:,.2f}".format(self.threshNew))
self.spinBox = QDoubleSpinBox()
self.spinBox.setMinimum(-2.3)
self.spinBox.setMaximum(99)
self.spinBox.setValue(self.threshNew)
self.spinBox.setSingleStep(0.02)
self.spinBox.valueChanged.connect(self.valueChang)
buttonBox = QDialogButtonBox(
QDialogButtonBox.Ok
| QDialogButtonBox.Cancel
| QDialogButtonBox.Apply)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(self.label2)
layout.addWidget(self.spinBox)
layout.addWidget(buttonBox)
self.resize(300, 200)
self.setLayout(layout)
okBtn = buttonBox.button(QDialogButtonBox.Ok)
okBtn.clicked.connect(self._okBtn)
cancelBtn = buttonBox.button(QDialogButtonBox.Cancel)
cancelBtn.clicked.connect(self.reject)
applyBtn = buttonBox.button(QDialogButtonBox.Apply) # +++
applyBtn.clicked.connect(self._apply) # +++
def _apply(self): # +++
print('Hello Apply')
def _okBtn(self):
print("""
Part : {}
Threshold : {}
ThreshNew : {:,.2f}""".format(
self.part, self.threshold, self.spinBox.value()))
def valueChang(self):
self.label2.setText("ThreshNew : {:,.2f}".format(self.spinBox.value()))
class Window(QWidget):
def __init__(self):
super().__init__()
label = QLabel('Hello Dialog', self)
button = QPushButton('Open Dialog', self)
button.clicked.connect(self.showDialog)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(button)
self.setLayout(layout)
def showDialog(self):
self.dialog = ModelessDialog(2, 55.77, self)
self.dialog.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = Window()
win.resize(300, 200)
win.show()
sys.exit(app.exec_())
What you are storing in the dlgButtons is just a list of enums, specifically the StandardButton enum, which is a list of identifiers for the buttons, they are not the "actual" buttons.
Also, you cannot use the clicked signal like this:
buttons.clicked(dlgButtons[0])
That will generate a crash, as signals are not callable. The argument of the clicked() signal is what will be received from the slot, which means that if you connect a function to that signal, the function will receive the clicked button:
buttons.clicked.connect(self.buttonsClicked)
def buttonsClicked(self, button):
print(button.text())
The above will print the text of the clicked button (Ok, Apply, Cancel, or their equivalent localized text).
What you're looking for is to connect to the clicked signals of the actual buttons, and you can get the individual reference to each button by using the button() function:
applyButton = buttons.button(qt.QDialogButtonBox.Apply)
applyButton.clicked.connect(self.applyFunction)

How to add widgets dynamically upon selecting an option from QComboBox in Pyqt5

I want to add widgets in GUI when a user selects a particular item from QComboBox.
With the different options in combo-box Pip config, I want GUI to look like as in the following images. In the right image, there are extra widgets present for an item Multi pip. Also I want the location of the extra widgets as shown in the right image.
How to add these widgets dynamically ? Please find the code below.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt, QRect
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
vbox = QVBoxLayout()
CpsLabel = QLabel()
CpsLabel.setText("<font size = 12>Cps</font>")
CpsLabel.setAlignment(Qt.AlignCenter)
CpsLabel.setTextFormat(Qt.RichText)
CpsPipConfigLabel = QLabel('Pip config: ')
CpsPipConfigComboBox = QComboBox()
CpsPipConfigComboBox.addItems(['Single pip', 'Dual pip', 'Multi pip'])
CpsPipConfigComboBox.setCurrentIndex(2)
CpsChannel = QLabel('Cps channel: ')
CpsChannelComboBox = QComboBox()
CpsChannelComboBox.addItems(['A', 'B', 'C', 'D'])
CpsChannelComboBox.setCurrentIndex(0)
CpsTotalTeethLabel = QLabel('Total teeth: ')
CpsTotalTeethEdit = QLineEdit()
CpsTotalTeethEdit.setFixedWidth(50)
CpsTotalTeethEdit.setPlaceholderText('18')
CpsTotalTeethEdit.setValidator(QIntValidator())
CpsMissingTeethLabel = QLabel('Missing teeth: ')
CpsMissingTeethEdit = QLineEdit()
CpsMissingTeethEdit.setFixedWidth(50)
CpsMissingTeethEdit.setPlaceholderText('1')
CpsMissingTeethEdit.setValidator(QIntValidator())
vbox.addWidget(CpsLabel)
vbox.addStretch()
CpsQHBox1 = QHBoxLayout()
CpsQHBox1.setSpacing(0)
CpsQHBox1.addStretch()
CpsQHBox1.addWidget(CpsPipConfigLabel)
CpsQHBox1.addWidget(CpsPipConfigComboBox)
CpsQHBox1.addStretch()
vbox.addLayout(CpsQHBox1)
vbox.addStretch()
CpsQHBox2 = QHBoxLayout()
CpsQHBox2.setSpacing(0)
CpsQHBox2.addStretch()
CpsQHBox2.addSpacing(20)
CpsQHBox2.addWidget(CpsTotalTeethLabel)
CpsQHBox2.addWidget(CpsTotalTeethEdit)
CpsQHBox2.addStretch()
CpsQHBox2.addWidget(CpsMissingTeethLabel)
CpsQHBox2.addWidget(CpsMissingTeethEdit)
CpsQHBox2.addStretch()
vbox.addLayout(CpsQHBox2)
vbox.addStretch()
CpsQHBox3 = QHBoxLayout()
CpsQHBox3.setSpacing(0)
CpsQHBox3.addStretch()
CpsQHBox3.addWidget(CpsChannel)
CpsQHBox3.addWidget(CpsChannelComboBox)
CpsQHBox3.addStretch()
vbox.addLayout(CpsQHBox3)
vbox.addStretch()
self.setLayout(vbox)
self.setGeometry(200, 100, 300, 300)
self.setWindowTitle('Steady state data processing')
self.setWindowIcon(QIcon('duty_vs_suction_map_sum.png'))
self.setAutoFillBackground(True)
p = self.palette()
p.setColor(self.backgroundRole(), QColor(255,250,100))
# p.setColor(self.backgroundRole(), Qt.blue)
self.setPalette(p)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I suggest you set the widgets up and place them at the beginning like you have them, but set them invisible. Then make a method that sets the appropriate widgets visible based on the qcombobox's current text and connect it to the qcombobox's activated signal.
You will also need to add self in front of almost every object so that it can be referred to from other methods.
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# setup code here...
self.CpsTotalTeethEdit.setVisible(False)
self.CpsTotalTeethLabel.setVisible(False)
self.CpsPipConfigComboBox.activated.connect(self.setup_total_teeth)
self.show()
def setup_widgets(self):
if self.CpsPipConfigComboBox.currentText() == "Multi pip":
self.CpsTotalTeethLabel.setVisible(True)
self.CpsTotalTeethEdit.setVisible(True)
By setting the items invisible instead of adding them with this method, you can also set them to be not visible when the cobobox's position is not for them.

Wrong widget order using vbox layout PyQt

I am trying to put a QLabel widget on top of (ie before) a QLineEdit widget edit.
But it keeps appearing after the QLineEdit widget. My code,
class CentralWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CentralWidget, self).__init__(parent)
# set layouts
self.layout = QtGui.QVBoxLayout(self)
# Flags
self.randFlag = False
self.sphereFlag = False
self.waterFlag = False
# Poly names
self.pNames = QtGui.QLabel("Import file name", self) # label concerned
self.polyNameInput = QtGui.QLineEdit(self) # line edit concerned
# Polytype selection
self.polyTypeName = QtGui.QLabel("Particle type", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("")
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
self.layout.addWidget(self.pNames)
self.layout.addWidget(self.polyNameInput)
self.layout.addWidget(self.pNames)
self.layout.addWidget(self.polyTypeName)
self.layout.addWidget(polyType)
self.layout.addStretch()
def onActivated(self, text):
# Do loads of clever stuff that I'm not at liberty to share with you
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.central_widget = CentralWidget(self)
self.setCentralWidget(self.central_widget)
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
self.central_widget.onActivated(text)
def main():
app = QtGui.QApplication(sys.argv)
poly = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The window I get is below.
What am I missing? I thought QVbox allowed to stack things vertically in the order that you add the items to the main widget. (Are these sub-widget objects called widgets?)
The problem is because you are adding self.pNames label to layout twice.
#portion of your code
...
self.layout.addWidget(self.pNames) # here
self.layout.addWidget(self.polyNameInput)
self.layout.addWidget(self.pNames) # and here
self.layout.addWidget(self.polyTypeName)
self.layout.addWidget(polyType)
self.layout.addStretch()
...
The first time you add the QLabel, it gets added before the LineEdit and when you add it second time, it just moves to the bottom of LineEdit. This happens because there is only one object of QLabel which is self.pNames. It can be added to only one location. If you want to use two labels, consider creating two separate objects of QLabel

Creating a 'Page View' style layout for a rich text editor with the scroll-bar aligned to the right edge

I'm trying to create a rich-text editor with a layout similar to Microsoft Word's 'Page View' or 'Print Layout.' I'd like to have a QTextEdit horizontally centered in the main window, with the scroll-bar aligned against the far right edge of the main window.
I couldn't find a way to move a QTextEdit's default scroll-bar independent of the QTextEdit itself. Instead, I tried creating a separate scroll-bar, and making the QTextEdit grow vertically using the solution found here: A QWidget like QTextEdit that wraps its height automatically to its contents?
Here is my attempt:
import sys
from PySide import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
cw = CentralWidget()
self.setCentralWidget(cw)
self.setGeometry(200, 200, 1000, 600)
self.show()
def resizeEvent(self, event):
self.centralWidget().setFixedHeight(event.size().height())
class CentralWidget(QtGui.QWidget):
def __init__(self):
super(CentralWidget, self).__init__()
self.initUI()
def initUI(self):
text = MainTextEdit()
text.setMinimumWidth(850)
text.setStyleSheet('border: 0;')
pageWidget = QtGui.QWidget()
scroll = QtGui.QScrollArea()
scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scroll.setMaximumWidth(18)
# If you change setWidgetResizeable to False,
# the textedit will center, but scrolling will not work.
scroll.setWidgetResizable(True)
scroll.setWidget(pageWidget)
hbox = QtGui.QHBoxLayout()
hbox.setContentsMargins(0,0,0,0)
hbox.addStretch(0.5)
hbox.addWidget(text)
hbox.addStretch(0.5)
pageWidget.setLayout(hbox)
hbox2 = QtGui.QHBoxLayout()
hbox2.setContentsMargins(0,0,0,0)
hbox2.addWidget(pageWidget)
hbox2.addWidget(scroll)
self.setLayout(hbox2)
class MainTextEdit(QtGui.QTextEdit):
def __init__(self, *args, **kwargs):
super(MainTextEdit, self).__init__(*args, **kwargs)
self.document().contentsChanged.connect(self.sizeChange)
self.setFontPointSize(80)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
def sizeChange(self):
docHeight = self.document().size().height()
self.setMinimumHeight(docHeight)
def main():
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
There are at least two problems with this:
Problem #1
As is, the code above does not horizontally center the QTextEdit in the main window, but the scroll bar at the far-right does work. If you change scroll.setWidgetResizable(True) to scroll.setWidgetResizable(False) on line 41, the QTextEdit will center horizontally, but the scroll-bar does not work. It seems you can get one feature or the other, but not both.
Problem #2
In order to keep the MainWindow from auto-expanding when the QTextEdit grows, the MainWindow assigns a fixed height to the CentralWidget whenever the MainWindow is resized (see line 19 of the code above). This works well until the user tries to vertically shrink the main window. The window can be vertically expanded by clicking and dragging the bottom border, but it can't be vertically shrunk.
Conclusion
Maybe this is the wrong approach all-together. Any suggestions?
Set a symmetrical margin via setViewportMargins on the QTextEdit which inherits from QAbstractScrollArea.
Example:
from PySide import QtGui, QtCore
app = QtGui.QApplication([])
window = QtGui.QWidget()
layout = QtGui.QVBoxLayout(window)
edit = QtGui.QTextEdit('jfdh afdhgfkjg fdnvfh vklkfjvkflj lddkl ljklfjkl jvkldjfkvljfgvjldf ll dl dljvklj ljljlbl llkb jbgl')
edit.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
edit.setViewportMargins(30, 0, 30, 30)
layout.addWidget(edit)
window.show()
app.exec_()
Gives:

Categories